2015年5月31日 星期日

第 23 周記事

本周閒暇時間都花在寫程式, 週日回家又因午後下陣雨, 也是躲在屋內寫程式. 還好近午時看天色不對, 叫喚二哥幫忙將路邊兩棵芒果樹套袋, 但二十分鐘不到就下起雨來了, 根本沒套完.

因下周菁菁畢業考, 所以週日早上我一個人上市集買菜. 上週二哥的手機在鄉下突然不見, 本周回來到處找, 遍尋不著, 真是奇怪. 上週我跟姐姐與菁菁都出去買菜, 回來時二哥就說找不到手機. 我們出去時他不是還在用嗎? 不過這也好, 這樣就不會一直想玩遊戲. 但是手機 SIM 卡還有儲值數百元, 所以還是得找出來才行 (二哥該不會懷疑是我偷藏起來吧? 我若是要禁止, 就會要他交出來, 不會偷藏).

週日早上把同事給我的木瓜樹種在菜園, 這樣總共有四棵了, 希望年底能開始收成. 傍晚小舅他們來了, 幫忙把他們寄放的舊流理台搬出來清洗, 因為小木屋也許再過半個月就完工了.

稻穀幾天前收割, 田裡鋪滿了切斷後的稻稈, 我突然想起書本有提到, 日本農人常用稻稈鋪在冬瓜藤蔓下, 一來使冬瓜不會直接碰觸泥土, 二來可減少雜草叢生情形. 所以趁天尚未黑, 趕緊一簍簍將田裡的碎稻稈鋪在菜園空地上 :


下周再將藤蔓區也鋪滿, 反正下一期稻作申請轉作, 不會犁田, 所以稻稈都會在田裡.


2015年5月30日 星期六

Easyui 測試 : Combobox 下拉式選單

今天在修改財工程式時遇到 Combobox 的運用問題, 在實際系統中測試變數較多, 環境較複雜, 不容易掌握問題的原因, 因此花一點時間單獨來測試一下 Combobox.

根據 Easyui 文件, Combobox 可以用兩種 HTML 元件轉化, 一是 SELECT-OPTION 下拉式選單, 二是 INPUT 元件, 只要指定 class 為 easyui-combobox 即可. 用 SELECT-OPTION 當然是最直覺的, 如下列範例 1 所示 :

範例 1 : http://mybidrobot.allalla.com/easyuitest/easyui-combobox-1.htm [看原始碼]

  <select id="category" class="easyui-combobox" name="category" data-options="panelHeight:'auto'">
      <option value="水泥">水泥</option>
      <option value="食品">食品</option>
      <option value="塑膠">塑膠</option>
      <option value="鋼鐵">鋼鐵</option>
      <option value="電子" selected>電子</option>
      <option value="金融">金融</option>
  </select>


此處 combobox 的屬性就放在 data-options 中, 這裡 panelHeight 設為 'auto', 表示會視選項多寡自動調整拉把的高度, 也可以直接指定一個數值 (例如 100 或 '100px', 有 px 須視為字串), 這樣高度就會被固定. 利用 OPTION 的 selected 屬性就可以設定初始值了. 注意, combobox 預設即有 autocomplete 功能, 輸入任何一個字首, 就會顯示過濾後的選項了.

此外也可以用 Javascript 來設定 data-options 中的屬性值, 如範例 2 所示 :

範例 2 : http://mybidrobot.allalla.com/easyuitest/easyui-combobox-2.htm [看原始碼]

  <select id="category" class="easyui-combobox" name="category">
      <option value="水泥">水泥</option>
      <option value="食品">食品</option>
      <option value="塑膠">塑膠</option>
      <option value="鋼鐵">鋼鐵</option>
      <option value="電子" selected>電子</option>
      <option value="金融">金融</option>
  </select>
  <script language="javascript">
    $(document).ready(function(){
      $("#category").combobox({
        panelHeight:'80px',
        panelWidth:150
        });
      });
  </script>


此例我們將 panelHeight 改為 '80px' (注意, 須用引號括起來), 但選項長度超過 80px, 所以會出現垂直滑動桿. 當然也可以全部都用 Javascript 設定, 但這最好使用第二種元件, 也就是 INPUT 來製作, 如範例三所示 :

範例 3 : http://mybidrobot.allalla.com/easyuitest/easyui-combobox-3.htm [看原始碼]

  <input id="category" name="category">
  <script language="javascript">
    $(document).ready(function(){
      $("#category").combobox({
        valueField:'value',
        textField:'text',
        data:[
          {value:'水泥',text:'水泥'},
          {value:'食品',text:'食品'},
          {value:'塑膠',text:'塑膠'},
          {value:'鋼鐵',text:'鋼鐵'},
          {value:'電子',text:'電子',selected:true},
          {value:'金融',text:'金融'}
          ]
        });
      });
  </script>


此例中我們只放置了一個具有 id 屬性的 INPUT 元素, 然後在 combobox 方法中設定 valueField (option 的 value 屬性名稱), textField (option 的顯示字串), 以及 data (option 屬性陣列) 這三個屬性即可. valueField 與 textField 的值是任意定的, 只要能與 data 中的設定相符即可. 預設的選項要用 seleted:true 來指定.

注意, 全部使用 Javascript 設定時, 應該像範例 3 那樣使用 INPUT 元件, 不要用沒有 option 的 select 來做, 否則會變得扁扁的. 將範例 3 的 INPUT 改為如下測試即知 :

<select id="category" name="category"></select>

以上都是使用本地端資料, combobox 也可以由遠端提供動態選項資料, 這必須用到 url 這個屬性, 若需要本地提供參數, 也需要用到 method 與 queryParams 屬性, 如範例 4 所示 :


  <input id="category" name="category">
  <script language="javascript">
    $(document).ready(function(){
      $("#category").combobox({
        panelHeight:'auto',
        valueField:'value',
        textField:'text',
        url:"get_combobox_category.php"
        });
      });
  </script>


此例中我們透過 url 指定由遠端程式 get_combobox_category.php 提供選項資料, 不過須先在後端資料庫建一個 category 資料表, 裡面只有一個欄位 category, 填入上面本地資料的六種類別, 此 category 資料表匯出內容如下 :

--
-- 表的結構 `category`
--

CREATE TABLE IF NOT EXISTS `category` (
  `category` varchar(255) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- 轉存資料表中的資料 `category`
--

INSERT INTO `category` (`category`) VALUES
('水泥'),
('食品'),
('塑膠'),
('鋼鐵'),
('電子'),
('金融'),
('金融');

然後就可以撰寫輸出此 category 資料的程式 get_combobox_category.php 如下 :  

<?php
header('Content-Type: text/html;charset=UTF-8');
$host="mysql.1freehosting.com"; 
$username="easyui_test";
$password="blabla";
$database="easyui_test";
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$SQL="SELECT * FROM `category`"; 
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$arr=array(); 
for ($i=0; $i<mysql_numrows($result); $i++) { //走訪紀錄集 (列)
     $row=mysql_fetch_array($result); //取得列陣列
     if ($i==4) { //指定第 5 個為預設值
       $arr[$i]=array("value" => $row["category"],
                      "text" => $row["category"],
                      "selected" => true);
       }
     else {
       $arr[$i]=array("value" => $row["category"],
                      "text" => $row["category"]);
       }
     } 
echo json_encode($arr);  //將陣列轉成 JSON 資料格式傳回
?>

此程式會輸出一個 JSON 物件陣列 :

[{"value":"\u6c34\u6ce5","text":"\u6c34\u6ce5"},{"value":"\u98df\u54c1","text":"\u98df\u54c1"},{"value":"\u5851\u81a0","text":"\u5851\u81a0"},{"value":"\u92fc\u9435","text":"\u92fc\u9435"},{"value":"\u96fb\u5b50","text":"\u96fb\u5b50","selected":true}, {"value":"\u91d1\u878d","text":"\u91d1\u878d"},{"value":"\u91d1\u878d","text":"\u91d1\u878d"}]

此處第五項多了一個 selected 屬性, 設為 true 即為預設值. 好了, combobox 就暫時測試到這裡, 總算釐清 combobox 中文傳回值可正常運作無疑, 要回頭檢視財工程式到底哪裡弄錯以至於無法顯示選項, 以後有空再繼續整理測試結果. 

# 2015-05-30 21:50:50 註 :

晚上在鄉下測試好久, 終於找到原因了, 原來問題出在 mode 這個屬性沒有設, 預設是 local, 這樣 combobox 只會在載入的資料的 text 中 (不是 value), 搜尋以輸入字開頭的選項. 參閱 Easyui 文件 combobox 的 mode 屬性說明 :

"Defines how to load list data when text changed. Set to 'remote' if the combobox loads from server. When set to 'remote' mode, what the user types will be sent as the http request parameter named 'q' to server to retrieve the new data."

也就是說, 當 mode 為 remote 時, 輸入查詢字時會以 http 要求將此查詢字串用 q 這個參數傳給後端伺服器. 為了說明這個問題, 我先準備了一個含有台股股票代號與公司名稱的資料表 stocks_list :

--
-- 表的結構 `stocks_list`
--

CREATE TABLE IF NOT EXISTS `stocks_list` (
  `stock_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `stock_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`stock_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- 轉存資料表中的資料 `stocks_list`
--

INSERT INTO `stocks_list` (`stock_id`, `stock_name`) VALUES
('0015', '富邦'),
('0050', '台灣50'),
('0051', '中100'),
('0052', 'FB科技'),
('0053', '寶電子'),
('0054', '台商50'),
('0055', '寶金融'),
....
('9958', '世紀鋼'),
('2342', '茂矽'),
('2364', '倫飛'),
('2816', '旺旺保');

然後我希望在 combobox 中, 輸入股票代號的第一個字元時就顯示以其為首的所有股票; 或者當輸入公司名稱的任一字元時, 就顯示所有含有此字元的股票. 亦即, 前者在 SQL 搜尋條件是 "$q%"; 而後者是 "%$q%", 如下面範例 5 所示 :

範例 5 : http://mybidrobot.allalla.com/easyuitest/easyui-combobox-5.htm [看原始碼]

  <input id="stock" name="stock">
  <script language="javascript">
    $(document).ready(function(){
      $("#stock").combobox({
        panelHeight:150,
        valueField:'value',
        textField:'text',
        url:"get_combobox_stocks_list.php"
        });
      });
  </script>

此例中我們僅指定資料來源為遠端程式 get_combobox_stocks_list.php, 其內容如下 :

<?php
header('Content-Type: text/html;charset=UTF-8');
$host="mysql.1freehosting.com"; 
$username="easyui_test";
$password="blabla";
$database="easyui_test";
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$q=$_REQUEST["q"]; //combobox 送出的查詢參數
if (isset($q)) { //有搜尋
  $where=" WHERE `stock_id` LIKE '".$q."%' OR ".
         "`stock_name` LIKE '%".$q."%'";
  }
else {$where="";} //沒有搜尋,顯示全部
$SQL="SELECT stock_id,stock_name FROM stocks_list".$where;
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$arr=array();
for ($i=0; $i<mysql_numrows($result); $i++) { //走訪紀錄集 (列)
  $row=mysql_fetch_array($result); //取得列陣列
  $value=$row["stock_id"];
  $text=$row["stock_id"]." (".$row["stock_name"].")";
  $arr[$i]=array("value" => $value,"text" => $text);
  }
echo json_encode($arr);
?>

此程式回應的 value 屬性為股票代號, 而顯示的選項文字是像這樣 : 2330 (台積電). 輸入 2 會顯示所有股票代號開頭為 2 之選項, 但是當輸入股票名稱, 像 "台" 時, 卻沒有半個選項. 原因就是如上所言, combobox 的 mode 屬性預設是 "local", 它只會在載入的選項資料中, 從開頭去比對 text 的內容, 因為傳回的 text 屬性都是以代號開頭, 因此輸入名稱就不會有任何匹配的選項了.

解決之道就是加上 mode:"remote" 設定即可, 如範例 6 所示 :

範例 6 : http://mybidrobot.allalla.com/easyuitest/easyui-combobox-6.htm [看原始碼]

  <input id="stock" name="stock">
  <script language="javascript">
    $(document).ready(function(){
      $("#stock").combobox({
        panelHeight:150,
        valueField:'value',
        textField:'text',
        url:"get_combobox_stocks_list.php",
        mode:'remote'
        });
      });
  </script>


哈哈, 終於搞定這個困擾我許久的問題了. 原以為 easyui 的 combobox 處理中文有 bug, 前面範例 1~4 破除了這個疑慮, 沒想到是設定上的問題.

不過這種 remote 方式會增加許多 request 次數, 例如輸入一個 "台" 字就會產生三次 http 要求, 如下圖所示, 第一個是輸入 "ㄊ" 時傳出 q 參數, 第二個是輸入 "ㄞ" 時傳出 q="ㄊㄞ" 給後端, 最後一個是傳出 q="台" :


最後, 也最重要的是, 當使用者選定選項後必須要去執行一項工作 (不然是選好玩的嗎), 這就要用到 onSelect 這個屬性, 其值是一個回呼函數, combobox 會傳入一個選項參數物件 item (不一定用此名稱, 我們可任意命名傳入參數, 例如 option), 利用 valueField 與 textField 所定義的值與顯示文字欄位名稱, 就可取得被選取項目的 value 與 text. 例如 valueField 與 textField 分別定為 "value" 與 "text", 則 item.value 與 item.text 就是被選取項目的值與顯示文字, 如範例 7 所示 :

範例 7 : http://mybidrobot.allalla.com/easyuitest/easyui-combobox-7.htm [看原始碼]

  <input id="stock" name="stock">
  <script language="javascript">
    $(document).ready(function(){
      $("#stock").combobox({
        panelHeight:150,
        valueField:'value',
        textField:'text',
        url:'get_combobox_stocks_list.php',
        mode:'remote',
        onSelect:function(item){
          $.messager.alert("Info", item.value + " " + item.text);
          }
        });
      });
  </script>


實際運用上可能是去更新 datagrid 的內容, 例如 :

      onSelect:function(item){
        $('#daily').datagrid("load",{
          op:"list_daily",
          stock_id:item.value
          });
        }

此處就是傳出 op 與 stock_id 這兩個參數去載入 id 為 daily 的 datagrid 內容.

參考 :

EasyUI的ComBoBox怎么设定默认值?


2015年5月29日 星期五

GAE 出現 Server Error 問題

今天財工程式告一段落, 把 GAE 的 cron table 修改後上傳 Google 讓心跳啟動, 但是連線 GAE 網站卻發現 Server Error :

Error: Server Error

The server encountered an error and could not complete your request.Please try again in 30 seconds.


事實上過了 30 秒再連線也是無濟於事, 因為這是我的 Python 程式有問題之故. 到 "Logs" 檢視 Error 原因, 發現是抓取信用額度總量管制餘額表的程式 fetch_twse_daily_debit.php 時出現錯誤 :

Traceback (most recent call last):
  File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 240, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 299, in _LoadHandler
    handler, path, err = LoadObject(self._handler)
  File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 85, in LoadObject
    obj = __import__(path[0])
  File "/base/data/home/apps/s~appfog-twstockbot/1.384633447891051628/main.py", line 228, in 
    ('/fetch_twse_daily_debit', fetch_twse_daily_debit),
NameError: name 'fetch_twse_daily_debit' is not defined

 原來我在 webapp2.WSGIApplication 中指派了 fetch_twse_daily_debit 這個 URL 所對應的 Class 名稱, 但是卻沒有定義這個類別的內容 :

    ('/fetch_twse_daily_institutes', fetch_twse_daily_institutes),
    ('/technical_analysis', technical_analysis),
    ('/create_daily_report', create_daily_report),
    ('/fetch_daily_report', fetch_daily_report),
    ('/fetch_twse_daily_debit', fetch_twse_daily_debit),

所以只要補定義此類別即可 :

class fetch_twse_daily_debit(webapp2.RequestHandler):
    def get(self):
        url="http://mystockbot.com/cron/fetch_twse_daily_debit.php"
        result=urlfetch.fetch(url, deadline=60)
        if result.status_code == 200:
            self.response.write(result.content)
        else:
            out="urlfetch failed"
            self.response.write(out)

這樣 GAE 就可正常運作了, 如下圖所示, 在 cron 啟動後的三個多小時因為程式錯誤導致所有 Request 都收到 500 Server Error 回應 (紅色), 改正後全部恢復正常的藍色了 :


先跑一個星期再來看看還要改哪裡.


2015年5月27日 星期三

第 22 周記事

今天已是週三了, 因為專心寫程式, 所以上週週記都沒時間寫. 過去這一周豪雨不斷, 雖然旱象解除, 但也造成一些災難. 因為下雨沒辦法出去菜園幹活, 所以專心寫財工程式, 自從四月底發現 000a.biz 停掉我的軟體機器人後, 我就回頭來改寫, 把整個專案移植到 Appfog 去. 因此 Arduino, 風力發電機, 水車都要先晾一邊了, 雖然每次看到那一票書跟板子零件都會心癢癢的.

菜園南邊竹架上的胡瓜 (小舅帶回來種的) 最近結果豐碩, 每周都有兩三顆可採, 因為長在竹架上, 所以有時沒注意就會太老, 只好拿來留種.


茄子也是不斷開花結果, 只要過了採收期就會由深紫色變淡, 壓起來變硬, 這樣就不好吃了, 只好丟掉 : 


現在菜園都佈滿冬瓜藤蔓, 走進菜園都要躡手躡腳, 免得踩到藤蔓與花朵 :


年初從公司園子裡挖回來的山蕉已長到與我同高, 果然作物最需要的是陽光與肥料, 以前在公司的樹林裡, 周遭都被高大的樹木遮住陽光, 根本長不大 :


等本周天氣放晴, 要動手來清理路邊的甘蔗園與香蕉園, 把老香蕉樹砍掉, 這樣新發的芽才會長得好. 


2015年5月20日 星期三

智利陸軍的障礙接力賽

昨天在 Youtube 看到這個影片, 讓我對智利刮目相看, 原來人家是這樣訓練軍隊的!


想想我們的五百障礙簡直是小兒科呢! 以前服預官役時最怕背著步槍爬竿, 因為全副武裝太重, 我臂力差根本爬不上去, 會被阿兵哥笑. 如果我們也用人家智利這套, 我看我會逃官吧!

2015年5月18日 星期一

第 21 周記事

本周為了姊姊下個月學校要去蘇州交流跑了兩趟領務局, 一次是補交照片, 過了兩天又通知說身分證是舊的, 想起去年底有申請補發, 姊姊拿給我的是後來又找到的舊身分證, 領務局核發護照時會向戶政單位查核身分證件, 所以又跑第二趟. 台胞證要等護照下來, 再委託旅行社辦理, 雄獅價錢是 $1500, 台北明陽 $1300 :

# 申請台胞證

週六菁菁去百博做入班測驗, 她的成績與程度可能會比姊姊那時差吧! 我們家小狐狸數理都不太行.

昨天聽小舅媽說, 大阿姨的外勞似乎沒在煮, 常常一大堆菜放壞掉全部丟垃圾車, 兩個人竟然吃醬油拌飯! 難不成我每周拿去的菜都白費了? 那真是可惜了. 原本我對此印尼籍外勞塞噴娜印象甚佳, 看起來很老實, 只是語言不通, 我用英文也無法與其溝通, 交代她如何煮她也聽不懂. 請外勞的麻煩就是這樣. 舅媽說不如送養護中心, 但是大阿姨願意去嗎? 這一代的老人真是可憐, 介於大家庭與小家庭, 農業與工業社會的遞變世代, 他們奉養父母終老, 但等自己老了, 時代卻變了, 沒有大家庭了, 孩子在新竹台北工作, 甚至是深圳, 北京, 紐約. 只能被送去養老院.

本週把財工的程式做了大改變, 恢復 2011 年原本的每日追蹤表, 甚至加上每月追蹤表, 希望 Appfog 不會對此做出限制才好, 大不了必要時花錢租虛擬主機亦可.

週日又去買了 100 張芒果袋, 傍晚繼續套袋, 到六點還是沒套完, 因為要煮菜了, 只好下周繼續. 只是芒果越來越大顆, 太晚套的話, 搞不好已被蟲蟲入侵了.

菜園現在被冬瓜藤四處攀爬, 令人舉步維艱, 怕踩到藤蔓. 冬瓜開黃色的花, 我查 "第一次種菜" 這本書, 說跟南瓜一樣,  最好在開花時進行人工授粉, 將熊花採下來, 撕開花瓣會看到裡面雄花的蕊, 將其與雌花的蕊柱接觸即可授粉. 雌花和雄花的差別是, 雌花底下有一個圓鼓鼓肥大的子房. 正面結構也有差異, 如下取自 "芒果树下种冬瓜,双丰收" 照片所示 :


昨天下午要做冬瓜封, 發現冰箱的甘蔗不夠了, 只好去蔗園砍一株來用. 但找來找去竟然沒看到堪用的南崗蔗, 但找到一株西洋蔗, 砍下來後削皮時發現非常脆, 很好削, 而且非常甜, 這拿來做料理實在太可惜了, 應該當水果吃. 我把頭尾切下拿去菜園種, 這樣的好品種一定要留下來.

雨季就快要來了, 菜園我看就只種冬瓜, 茄子, 空心菜與地瓜葉就好.


2015年5月16日 星期六

十分鐘讓你看懂科技性失業

今天在 Tsung's Blog 看到這短片, 探討自動化科技不斷進步下, 未來的人們將會面臨 100 年前汽車工業發展後, 馬兒所面臨的科技性失業問題. 未來不僅硬體機器人會奪走大多數藍領的工作, 軟體機器人也將讓很多白領階級丟掉飯碗.

https://www.youtube.com/watch?t=895&v=uAvbinkTqzY


我感到最有興趣的是, 在這短片大約 7 分 30 秒左右, 談到未來具有人工智慧的軟體機器人, 將會具備自我學習能力, 也就是說會自行寫出具有先進演算法的軟體, 也能夠修正本身的瑕疵與錯誤, 使自己不斷進化 ... 天啊, 太可怕了, 軟體工程師也將沒搞頭了.

參考 :

# How to Teach Computers to Learn on Their Own
# Machines that Think for Themselves - Yaser S. Abu


2015年5月15日 星期五

兩篇 Raspberry Pi 實作論文

今天在聯合大學網站上找到這篇樹莓派實作論文 :

遠端農場環境監控

此文利用樹莓派板子透過 GPIO 來讀取農場的溫濕度, 操控 webcam, 以及透過繼電器控制水閥與燈光. 軟體部分採用 Node.js 與 Express.IO 來建構後端平台與資料庫, 透過 Linux 本身的 cron job 定時更新環境資料.

上期商周封面文章探討全球瘋農業, 其中談到物聯網在未來幾年將會在農業應用上發展得最快. 這篇論文正好呼應了這個展望. 現在國外許多農夫已經開始運用 IT 與 IOT 技術在農場經營了, 若結合太陽能供電與行動上網, 農場遠距營運管理將會越來越紅火.

第二篇是南台科大的這篇 :

以 Raspberry Pi 為基礎之遠距無線監控

此文所實作之監控車, 好比是個活動的監視器, 透過家中的 WIFI 信號進行操控, 使其成為一個行動保全. 若能像掃地機器人那樣在弱電源時自動回座充電就更完美了.


小森食光

上周六吃午飯的時候, 我在公視看到這部電影的預告片, 描述橋本愛飾演的主角市子, 在結束一段戀情後, 從都市回到東北的老家小森, 過著自給自足的農耕生活, 依循以前對母親的記憶, 調理出各種好吃的料理.

種植, 採集, 料理, 就是本片的主軸, 自食其力的田園生活, 對都市長大的人而言, 可能會覺得很寂寞與無聊, 對我來說是熟悉不過了, 但不是我親力親為, 而是多年以來看著母親自己養雞養鴨與種菜, 吃的食物大都是自己田裡生產的作物. 如今, 我也開始了假日親手耕種的生活, 雖然還只是一小步而已.


看到片中橋本愛吃番茄的神情, 讓我覺得, 食物真正的味道不需要言語來形容, 只要用舌頭深度去品味. 據說她在片中做了 20 道料理, 光是預告片中的冰鎮番茄就讓我學到一招了.

真迫不及待想要去看. 但高雄到底要何時才會上映呢?

# 綠野,時光,與生活的真味——專訪橋本愛《小森食光》
# 療癒系電影 「小森食光」用手造暖感填補虛空心靈
# Youtube : 小森食光
小森食光1(電影《小森食光》原著作品)


關於 MySQL 的限制

今天為了確認 MySQL 對資料表名稱的限制, 整理了網路上查到的資料如下 :

資料庫與資料表名稱的限制 :
  1. 只使用英數字與底線之組合 (小於 64 字元), 且不可用數字開頭 
  2. 有分大小寫
欄位名稱的限制 :
  1. 只使用英數字與底線之組合 (小於 64 字元), 且不可用數字開頭
  2. 不分大小寫, 故 USER 與 user 是同一欄位
之前有試過用 CREATE TABLE 產生名稱為數字的資料表 (例如股票代號), 發現建立資料表沒問題, 但卻無法正常寫入資料.

另外, 最多可建多少個 MySQL 資料庫, 以及一個資料庫最多可建幾個資料表呢? 根據 "Limits on Number of Databases and Tables" 這篇說明, MySQL 基本上對此並無限制, 但可能會受到作業系統的檔案系統限制; 而儲存引擎 Innodb 則允許最多 400 萬個資料表.

"MySQL has no limit on the number of databases. The underlying file system may have a limit on the number of directories.

MySQL has no limit on the number of tables. The underlying file system may have a limit on the number of files that represent tables. Individual storage engines may impose engine-specific constraints. InnoDB permits up to 4 billion tables."

某些免費主機會限制能建立的資料表數目, 超過就會被停權. 而 Appfog 的 MySQL 限制如下 :

https://support.appfog.com/hc/en-us/articles/202315676-MySQL 

參考 :

# MySQL 教材:建立/描述/刪除表格
mysql一個資料庫最可以幾個表
# Limits on Number of Databases and Tables
# MySQL 超新手入門(9)表格與索引 (讚)

意外找到張益裕的部落格, 十多年前開始學 Java 時, 買過他所翻譯的書, 非常厚, 可說是我走向軟體之路的啟蒙書.

# Python Tutorial 第一堂(1)揭開序幕


2015年5月14日 星期四

PHP 的 $_GET, $_POST, 與 $_REQUEST 測試

最常用的 HTTP 方法是 GET 與 POST, 當我們提交表單時, 後端伺服器上的 PHP 程式可以分別用 $_GET["param"] 與 $_POST["param"] 來取得前端傳來的參數 param, 如果不知道前端會用哪一種方法, 則可用 $_REQUEST["param"], 不管前端用 GET 還是 POST, $_REQUEST 都可以擷取到參數.

下面以三個範例來測試 $_GET, $POST, 與 $_REQUEST. 首先寫一個含有表單元素的網頁 http_get_form.htm :

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>PHP Test</title>
</head>
<body>
  <form method="get" action="http_test.php" name="form1">
    <input type="text" name="name" value="">
    <input type="submit" value="確定" name="ok"> 
  </form>
</body>
</html>

在此網頁中, 表單採用 GET 方法傳送元素, 表單內只含有一個名稱為 name 的輸入元素, 當按下確定後會將此表單向後端程式 http_test.php 提交, 其內容如下 :

<?php
header('Content-Type: text/html;charset=UTF-8');
$request=$_REQUEST["name"];
$post=$_POST["name"];
$get=$_GET["name"];
echo "<br>";
echo "REQUEST=".$request."<br>";
echo "POST=".$post."<br>";
echo "GET=".$get;
?>

此程式中分別用 $_GET, $POST, 與 $_REQUEST 取得前端傳出來的 name 參數, 然後向前端輸出, 如下面範例 1 所示 :

測試範例 1http://mybidrobot.allalla.com/phptest/http_get_form.htm [看原始碼]

後端程式 http_test.php 輸出結果如下 :

REQUEST=get_form
POST=
GET=get_form

可見以 GET 方法提交時, 必須用 $_GET 或 $_RQUEST 讀取, 不能用 $_POST.

如果我們將 GET 改為 POST, 如下 http_post_form.htm :

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>PHP Test</title>
</head>
<body>
  <form method="post" action="http_test.php" name="form1">
    <input type="text" name="name" value="">
    <input type="submit" value="確定" name="ok"> 
  </form>
</body>
</html>

同樣向 http_test.php 提交, 如下範例 2 所示 :

測試範例 2 : http://mybidrobot.allalla.com/phptest/http_post_form.htm [看原始碼]

結果如下 :

REQUEST=post_form
POST=post_form
GET=

可見用 POST 方法提交只能用 $_POST 或 $_REQUEST 取得參數, 不能用 $_GET.

還有一種 GET 方法是使用超連結發出 Request, 不是使用表單的, 如下 http_get_url.htm 所示 :

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>PHP Test</title>
</head>
<body>
  <a href="http_test.php?name=uuu">http_test.php?name=uuu</a>
</body>
</html

如下面範例 3 :

測試範例 3 : http://mybidrobot.allalla.com/phptest/http_get_url.htm [看原始碼]

結果與範例 1 相同, 只能用 $_GET 或 $_REQUEST 才能取的參數 :

REQUEST=uuu
POST=
GET=uuu

經過這三個測試, 可知不管是 GET 與 POST 方法, 一律用 $_REQUEST 就沒問題了.


2015年5月11日 星期一

第 20 周記事

姊姊下周要段考, 二哥要去慈濟勞動服務, 所以本周只有我跟菁菁回鄉下.

週六要回去時, 想說順路去特力屋買風力發電機用的木板底座, 所以捨自由路走民族路, 哪知要上惡名昭彰的民族大中路交流道竟然走了半小時, 當然特力屋也沒去.

每次從這個交流道上國十就不禁罵這是哪個白癡官員設計的? 民族路沒有設地下道不說, 一上交流道就跟高鐵那邊來的車流來個剪刀交越, 往鳳山的車流與往旗山方向的車流要互相交叉, 怎會不塞呢? 到底是誰規劃設計的? 真的, 錯誤的政策比貪汙還可惡!

週日小安把新監視器拿來, 共 $13800 元, 而且估量了菜園方向加裝一支監視器所需的線長. 菜園的茄子已可收成, 本周計採收五條, 部分拿去送給大阿姨 :



 芒果套袋部分, 我又去富億買了 100 張紙袋, 結果只套了兩棵芒果樹, 還是沒套完, 真是太驚人了, 用目測的話以為大約 60 顆芒果, 但實際上遠遠超過估計, 一株芒果樹大概可產 80~90 顆哩! 只好下周再買 100 張, 今年芒果大豐收! 以前都是媽在套袋, 我們負責吃, 根本不了解家裡的六株芒果樹年產多少.


那些太高或太裡面的沒辦法觸及只好放棄, 沒辦法套袋. 還有就是像粽子那樣好幾顆綁在一起的, 中型袋也裝不下, 必須用左營阿姨拿來的大袋子 :


本來打算這周完成風力發電機旋轉底部的, 週日下午去製材所繞了一圈, 有看到合用的木板, 可惜假日工廠無人, 想買也沒得買. 晚上將上週買的旋轉輪拆了, 以便比對一下是否能鎖在角鐵上 :


原來底輪也是一個鋼珠軸承, 這樣一個賣 50 元不算貴啦. 週日早上經過五金行時有去問要做保護蓋的塑膠管價格, 一尺 17 塊, 我想這樣大概就夠長了.



2015年5月10日 星期日

母親節

昨天去提拉米蘇買了四個芒果口味的小蛋糕, 今早與菁菁去慈恩塔給媽過母親節. 第一個沒有母親的母親節.

去年最後一個母親節時, 本來打算找全部阿姨跟媽一起去漢神巨蛋吃大餐, 但最終未成行. 雖然大熱天要跑來高雄, 媽大概也是會說不要, 但若多一點堅持, 或許會成功, 就像她跟小舅去日本玩一樣. 我是說團費已繳, 要退可能很麻煩. "明年再去好了", 母親節姊妹聚餐沒辦成時我是這麼想, 但人生中有些契機只有一次.

我從來沒跟母親說過我愛你, 相信那時代的人大部分都說不出口. 我是在安寧病房照顧她的某個晚上, 坐在椅子上望著在嗎啡藥力下已陷入昏睡的她, 我知道母親來日已無多, 就在幫她擦完臉後, 怯懦地在她耳邊說了我愛你.

雖然不知道她那時還聽得到否, 但我終究是說了.


2015年5月8日 星期五

Highstock 測試 (一)

稍微搞懂 Highcharts 後, 我迫不及待要來測試它用來繪製股票資料的 Highstock. 其環境配置與 Highcharts 一樣, 下載 zip 檔解開後將 js 資料夾下的 highstock.js 複製到專案目錄下匯入網頁中即可. 參考 :

# Highchart 執行環境配置


只要在網頁中匯入 jquery.js 與 highstock.js 即可 :

  <script src="../jquery/jquery.js"></script>
  <script src="../jquery/highstock.js"></script>

或者利用 CDN 供檔, 就毋需下載 SDK 了 :

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="http://code.highcharts.com/stock/highstock.js"></script>

這樣接下來就可以進行測試了. 我將解壓縮目錄 examples/candlestick 下的 index.html 改編為如下範例 1 :

範例 1 : http://tony1966.xyz/test/jquerytest/highstock-1.htm [看原始碼]

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Highstock 測試</title>
  <script src="../jquery/jquery.js"></script>
  <script src="../jquery/highstock.js"></script>
</head>
<body>
  <div id="container" style="min-width: 600px; height: 400px; margin: 0 auto">
  </div>
  <script>
    $(function () {
      var url="http://www.highcharts.com/samples/data/jsonp.php?a=e&filename=" +
              "aapl-ohlc.json&callback=?";
      $.getJSON(url, function(data) {
        $('#container').highcharts('StockChart', {
          rangeSelector: {selected : 1},
          title: {text: 'AAPL Stock Price'},
          series: [{
            type: 'candlestick',
            name: 'AAPL Stock Price',
            data: data,
            dataGrouping: {units: [['week', [1]], ['month',[1, 2, 3, 4, 6]]]}
            }]
          });
        });
      });
  </script>
</body>
</html>

可見它是用 jQuery 的 getJSON() 向伺服器取得 JSON 格式資料後, 傳給回呼函式, 再餵給 series 之 data 屬性. 此 JSON 檔格式如下, 是一個二維陣列 :

http://www.highcharts.com/samples/data/jsonp.php?a=e&filename=aapl-ohlc.json&callback=?

?(/* AAPL historical OHLC data from the Google Finance API */
[
/* May 2006 */
[1147651200000,67.37,68.38,67.12,67.79],
[1147737600000,68.10,68.25,64.75,64.98],
[1147824000000,64.70,65.70,64.07,65.26],
[1147910400000,65.68,66.26,63.12,63.18],
[1147996800000,63.26,64.88,62.82,64.51],
[1148256000000,63.87,63.99,62.77,63.38],
[1148342400000,64.86,65.19,63.00,63.15],
[1148428800000,62.99,63.65,61.56,63.34],
[1148515200000,64.26,64.45,63.29,64.33],
[1148601600000,64.31,64.56,63.14,63.55],
[1148947200000,63.29,63.30,61.22,61.22],
[1149033600000,61.76,61.79,58.69,59.77],
/* Jun 2006 */
.....
.....
/* May 2013 */
[1367366400000,444.46,444.93,434.39,439.29],
[1367452800000,441.78,448.59,440.63,445.52],
[1367539200000,451.31,453.23,449.15,449.98],
[1367798400000,455.71,462.20,454.31,460.71],
[1367884800000,464.97,465.75,453.70,458.66],
[1367971200000,459.04,465.37,455.81,463.84],
[1368057600000,459.81,463.00,455.58,456.77],
[1368144000000,457.97,459.71,450.48,452.97]
]);


Highcharts 執行環境配置

今天在這個網站看到 Highcharts 的介紹, 對其功能大為驚豔 :

# 5个最顶级jQuery图表类库插件-Charting plugin

功能實在太強大了, 繪製的圖美美的, 非常 Professional. 這是一家來自挪威的小而美軟體公司 Highsoft AS 的產品, 利用 jQuery 與 HTML5 技術推出一系列資料視覺化工具 (Data Visualization Tools), 目前有五套 :
  1. Hightcharts : 用於繪製一般統計圖表
  2. Hightstock : 用於繪製金融資訊圖表
  3. Highmaps : 用於繪製地理圖表 (新產品)
  4. Highslide JS : 用於圖片視頻展示
  5. Highcharts Cloud : Highcharts 雲端內容提供平台 (免費試用中)
我目前最有興趣的是 Highstock, 不過還是得先從 Highchart 學起. Hightchart 源自該公司創辦人兼技術長 Torstein 為了顯示其下雪厚度的量測圖, 四處找尋適合的工具卻不可得, 才自行研發出 Highcharts. 當時正是 Flash 大行其道的時代, 但 Torstein 希望使用開放標準的非專利產品, Flash 顯然不敷需求. 於是在自行開發時使用的是開源的 HTML5 與 jQuery.

Higharts 採用 CC (Creative Commons) 創用授權, 只要遵循此要求, 對於個人及非營利用途, 可自由免費使用. 商業用途則每個網站收費 $90 元美金, 詳見 :

# https://shop.highsoft.com/highcharts.html

Highchrts 相關書籍目前有四本 :

# Learning Highcharts, Packt 出版
# Highcharts Cookbook, Packt 出版
# Highcharts Essentials, Packt 出版
# Beginning JavaScript charts with jqPlot, D3, and Highcharts, Apress 出版

前三本 Packt 的都是 Highcharts 專書, 而第四本 Apress 的主要是介紹 jqPlot 與 D3, 而 Highcharts 只是在第 18 章前面簡介一下如何從 jqPlot 過渡到 Hicharts 而已. 我個人覺得 jqPlot 較麻煩, 要引入一堆 js 檔, 設定上也比 Highcharts 囉嗦, 而檔案也沒有比較小.

使用 Highcharts 需先布置執行環境, 設定樣式表與函式庫來源. 若是本地網路使用, 請先下載 Highcharts :

# http://www.highcharts.com/download

解開後目錄結構如下 :


切到 js 目錄下, 其中第一個 hicharts.js 就是我們需要的檔案. 將其複製到專案目錄下, 我是放在 jquery 目錄下跟 jquery.js 放一起 :


這樣只要搭配 jQuery 就可以使用了. 下面參考 Document 中的範例 加以改編如下 :

範例 1 : http://tony1966.xyz/test/jquerytest/highcharts-1.htm [看原始碼]

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Highchart 測試</title>
  <script src="../jquery/jquery.js"></script>
  <script src="../jquery/highcharts.js"></script>
</head>
<body>
  <div id="container" style="min-width: 310px; height: 400px; margin: 0 auto">
  </div>
  <script>
    $(function () {
      $('#container').highcharts({
    $(function () {
      $('#container').highcharts({
        chart: {type: 'bar'},
        title: {text: '零食消耗統計'},
        xAxis: {categories: ['冰棒', '小熊餅乾', '泡麵']},
        yAxis: {title: {text: '被吃掉的零食'}},
        series: [
          {name: '小葵', data: [1, 2, 4]},
          {name: '小新', data: [5, 7, 3]}
          ]
        });
      });
  </script>
</body>
</html>


當然也可以從 CDN 取得函式庫, 其 CDN 位置如下 :

# Highchart CDN

只要把 script 部分改為如下即可 :

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="http://code.highcharts.com/highcharts.js"></script>

範例 2 : http://tony1966.xyz/test/jquerytest/highcharts-2.htm [看原始碼]

此處 jQuery 使用 Google 的 CDN 供檔.

# Google CDN (hosted library)

下面範例三折線圖改自解壓縮後的 examples\line-basics 下的 index.html :

範例 3 : http://tony1966.xyz/test/jquerytest/highcharts-3.htm [看原始碼]

參考 :

# 初探 HighCharts 畫報表圖的好幫手 教學
# [jQuery] Javascript plotting library 畫圖 chart

ExtJS 5 GPL 授權版下載

與 ExtJS 一別已近一年, 沒想到 Sencha 已經推出第五版了, 與我原先學的 4.2.1 版不知變化有多大? 但我已經回頭投入 jQuery 陣營了, 其實也沒時間再研究 ExtJS. 只是看到 ExtJS 商用版貴鬆鬆的報價 (5 人開發版 $3225, 台幣 9 萬 6 千多), 我想大概只有專業設計公司會花錢買吧 !

#  https://www.sencha.com/store/


在 Sencha 首頁到處找都看不到以前 Community GPL 免費版的下載頁, Google 了一下, 在論壇這篇找到了 :

Ext JS Community Forums 5.x : ExtJS 5 Release and Opensource download

原來是在首頁上方的 "Store/Licencing" 頁面中間, 有一個 "Sencha GPL Download Page", 點下去就會出現下載頁 :

http://www.sencha.com/legal/GPL/ 


輸入 Email 後按 "Download SDK", 再去收信, 點信裡的超連結就可以下載了. 我下載到的是 5.1.0 版, 超恐怖, zip 檔高達 92MB! 而 Sencha Touch 是 2.4.1 版, 也高達 55MB!


2015年5月7日 星期四

Appfog 出現 Operation Not Permitted 無法上傳更新問題

今天要將更新程式上傳 Appfog 主機時, 竟然出現下列錯誤訊息 :

E:\easyuicms>af update mysnowball
[WARNING] DL is deprecated, please use Fiddle
Error 200: Operation Not Permitted

我以為 Appfog 是否要對免費用戶施加更多流量限制, 把我的帳戶關閉了嗎? 但登入後台又一切正常哩! 真是奇怪. 後來在這篇找到原因了, 原來是太久沒重新登入了 :

Error 200: Operation not permitted

"Your .af_token may have expired. Please try and re-login using af login and that should resolve the issue for you. "

我都好久沒關機了, 命令視窗也從未關閉, 每次要下指令時都不需再登入一次, 為何今天就 ERROR, 奇怪.


用 PHP 計算移動平均線 MA 與指數移動平均線 EMA 的方法

這兩天實作完 KD 與 OBV 指標後, 接著對超簡單的移動平均線 MA 與有一點變化的指數移動平均線這兩種指標有點興趣了, 雖然隨便在哪一個理財網站都可以查到, 但那要動手查呀, 對懶人不利. 凡事都想自己動手做, 這就是 maker 無法克制的衝動.

所謂移動平均線就是過去數天收盤價的平均值, 表示那段期間買進者的平均持股成本. 常用的有短線的 MA5 (周線), MA10 (雙周線), 中線的 MA20 (月線), MA60 (季線), MA120 (半年線), 以及長線的 MA240 (年線) 等. 詳參 :

# 什麼是均線
# 從均線型態找出強勢股

首先要在追蹤資料表設定欄位, 我只看到季線而已 :

      $data_array["trade_date"]="date";                   //交易日 2015-05-07
      $data_array["close"]="float unsigned";           //收盤價
      $data_array["MA5"]="float";                          //5日移動平均
      $data_array["MA10"]="float";                        //10日移動平均
      $data_array["MA20"]="float";                        //20日移動平均
      $data_array["MA60"]="float";                        //60日移動平均
      $data_array["MADIF510"]="float";                //交叉(+黃金-死亡)
      $data_array["MADIF520"]="float";                //交叉(+黃金-死亡)

然後就可以進行計算了, MA5 顧名思義, 必須有五日以上收盤資料才行. MA5 的計算方法其實很簡單, 只要依交易日下降排序, 再取前五筆收盤價計算其平均值即可, 而且可以在查詢資料庫時, 利用 MySQL 的聚合函數 AVG 直接計算, 程式如下 :

    $SQL="SELECT * FROM ".$table." ORDER BY trade_date DESC";
    $RS1=run_sql($SQL);
    //計算 MA5  
    if (count($RS1) >= 5) { //有 5 筆以上資料才計算
      $trade_date=$RS1[0]["trade_date"]; //交易日期
      $SQL="SELECT AVG(close) FROM ".$table." ORDER BY trade_date DESC LIMIT 5";
      $RS2=run_sql($SQL);
      $MA5=round($RS2[0][0],2);  //取小數兩位
      //更新追蹤資料表
      $data_array["MA5"]=$MA5;  //紀錄今日 MA5 結果
      update($table,$data_array,"trade_date",$trade_date);
      $data_array=null;  //清空陣列
      $RS2=NULL;
      }
    else {echo "未達 5 筆收盤資料無法計算 MA5.<br>";}
    RS1=null;

其他 MA10, MA20, MA60 也是如此計算, 只要將 5 改成 10,20, 60 即可.

其次是 EMA 指數移動平均線, MA 雖然簡單易懂, 但卻有反應太慢的問題, 這是大部分落後指標共通的問題. 而 EMA 則是透過一個指數係數, 給予時間較近者較大之權值, 以提高移動平均線的靈敏度, 因此在大漲或大跌時, EMA 會迅速呈現較劇烈之變化, 當然有一好沒兩好, 詳參 :

# 指數平均線 (EMA) 是移動平均線的先鋒軍

根據這篇的介紹, EMA 常用參數為 EMA12 (快速) 與 EMA50 (慢速), 注意這裡的 12 與 50 雖然意義上是天數, 但那只是用來計算權重係數, 並不是說必須要有 50 天的收盤價才能進行計算. 計算 EMA 只要有一筆收盤紀錄即可, 計算公式如下 :

今日EMA=(今日收盤價-昨日EMA)*權重係數+昨日EMA
權重係數=2/(期間+1)

故 EMA12 之權重係數為 2/13, 而 EMA50 則為 2/51

首先須在追蹤資料表新增如下欄位 :

      $data_array["trade_date"]="date";                    //交易日 2015-05-07
      $data_array["close"]="float unsigned";            //收盤價
      $data_array["EMA12"]="float";                       //12日指數移動平均
      $data_array["EMA50"]="float";                       //50日指數移動平均
      $data_array["EMADIF"]="float";                     //EMA交叉(+黃金-死亡)

PHP 程式如下 :

    $SQL="SELECT * FROM ".$table." ORDER BY trade_date DESC";
    $RS1=run_sql($SQL);
    //計算 EMA12, EMA50 :
    if (count($RS1) != 0) { //有 1 筆以上資料才計算
      $trade_date=$RS1[0]["trade_date"]; //交易日期
      $close=$RS1[0]["close"]; //今日收盤價
      if (count($RS1) == 1) {
        $EMA12=$RS1[0]["close"]; //初值=收盤價
        $EMA50=$RS1[0]["close"]; //初值=收盤價
        $EMADIF=0;
        }
      else {
        $EMA12P=$RS1[1]["EMA12"]; //前一日 EMA12
        $EMA12=$EMA12P + 2/13*($close-$EMA12P);
        $EMA50P=$RS1[1]["EMA50"]; //前一日 EMA50
        $EMA50=$EMA50P + 2/51*($close-$EMA50P);
        $EMADIF=$EMA12-$EMA50;
        }
      //更新追蹤資料表
      $data_array["EMA12"]=$EMA12; //顯示時再取小數2位
      $data_array["EMA50"]=$EMA50; //顯示時再取小數2位
      $data_array["EMADIF"]=$EMADIF; //顯示時再取小數2位
      update($table,$data_array,"trade_date",$trade_date);
      $data_array=null;  //清空陣列
      }
    else {echo "無收盤資料無法計算 EMA.<br>";}
    RS1=null;

看起來似乎還比 MA 簡單呢, 因為不用再查一次資料表.

[2015-06-05 修正] :

昨晚檢查一周來財工程式的執行數據, 發現計算出來的平均值不正確, 在 Google 搜尋過後才搞清楚, 原來 MySQL 的 LIMIT 功能不是我想的那樣, 它是在最後才 apply 的, 例如下面的 SQL :

"SELECT AVG(close) FROM ".$table." ORDER BY trade_date DESC LIMIT 5";

MySQL 會先對 close 欄位的所有數據取平均值後, 再取前面五筆, 由於平均值結果只有一筆, 所以也就得到一筆資料, 但卻是全部 close 的平均, 而非最近五筆 close 的平均, 參考下列文章 :

Select average from MySQL table with LIMIT
# Subquery with limit clause : Sub query « Select Clause « SQL / MySQL
# Limit not working for avg query
avg() and limit

"you have to consider how LIMIT works. It's the last thing that happens as the query is executed."

所以, 上面所有計算平均值的 SQL 指令都必須修改, 例如 MA5 要改成 :

      $SQL="SELECT AVG(items.close) FROM (SELECT close FROM ".$table.
           " ORDER BY trade_date DESC LIMIT 5) AS items";

亦即要採取 sub-query 的兩層查詢方法, 先用一個 SELECT ... LIMIT  5 指令找出最近的五筆紀錄, 並命名為 items, 再用 AVG 去計算 close 欄位的平均值.


2015年5月6日 星期三

如何取得 MySQL 聚合函數 (Aggregate) 的傳回值

平常使用 MySQL 函式庫查詢資料表時, 如果有查到資料, 則 run_sql() 會傳回一個二維陣列, 第一維是數字索引, 表示各列資料 (即每一筆紀錄就是一個一維陣列); 第二維是以各欄位名稱為索引的關聯陣列, 所以只要用 is_array(), count() 函式配合迴圈就可以取得資料庫的傳回值 :

$SQL="SELECT * FROM users";
$RS=run_sql($SQL);
if (is_array($RS)) { //或 count($RS)!=0, sizeof($RS)!=0
  for ($i=0; $i<count($RS); $i++) {
     echo $RS[$i]["name"];
    }
  }
$RS=null;

如果只有一列傳回值, 當然只要用 $RS[0]["name"] 即可取得.

但昨天在寫 KD 的計算時卻遇到了這個問題 : 若 SQL 用到統計用的聚合函數, 該怎麼取得傳回值呢? 例如計算 KD 時須先找出近 9 日收盤最低價與最高價, 參考 :

計算 KD 值的方法

$SQL="SELECT MAX(close) FROM ".$table." ORDER BY trade_date DESC LIMIT 9";
$RS=run_sql($SQL);

照理說最大值 MAX 與最小值 MIN 傳回的應該是一個值, 而不是一群值, 不過 run_sql() 仍然是傳回了一個二維陣列, 用 var_dump() 即可顯示其結構如下 :

var_dump($RS);

array(1) { [0]=> array(2) { [0]=> string(4) "82.5" ["MAX(close)"]=> string(4) "82.5" } }

因此可以用下列兩種方式取得傳回值 :

$max=$RS[0][0];  //建議用這個較簡單



$max=$RS[0]["MAX(close)"];  //忘了吧

當然其他的聚合函式如 COUNT 用法也是一樣.


2015年5月4日 星期一

第 19 周記事

因二哥要準備下周段考, 本周仍是三人組回鄉下. 二哥四月份去補理化後, 成績有大躍進, 周排名從 12 進步到 4, 但有時又掉回 10 以後, 頗不穩定, 二哥只要不迷遊戲, 專心念書成績就會變好, 前三名只是要不要, 而不是能不能, 連遊戲都可以玩到出售帳號, 他真的是很厲害.

這個禮拜無訪客, 所以變得很清閒. 午覺起來之後, 驕陽如炙, 但風也是呼呼的吹, 打開房間的兩扇窗, 也稍解了些暑氣. 躲在房裡將追蹤程式做個翻修, 這一周暫時放下 Arduino, 回頭搞財工, 我的閒暇生活大致就是這樣切來切去.

看到風這麼大, 突然想起我做到一半的風力發電機, 於是跑到樓上將發電機安裝好, 強勁的西南風馬上讓風機轉起來, 最強時量得電壓高達 80V, 這要對 12V 電瓶充電絕對沒問題. 我現在打算把發出來的電供應屋旁水圳與後院菜園的夜間感應照明, 以及 Arduino 控制器使用.

四點之後到菜園巡視, 看到芒果已長至約拳頭大, 必須套袋才行, 但中型袋子已用完, 所以叫菁菁起床, 問她要不要跟我去農具行買袋子. 那家富德資材行占地頗大, 但每次經過都沒時間下去看, 老闆娘聽口音應該是中國新娘, 言談頗犀利. 芒果袋一包 100 張 50 元, 金煌的袋子較大較長, 一包 140 元. 我們家的芒果品種是海頓, 中型袋即可.

接著繞到五金行去找推車使用的轉向輪, 想將風力發電機的固定底座改成旋轉式的. 因為我發現頂樓風向有時西南, 有時正西, 而目前固定基座是朝向西南, 所以當風向改為正西時, 即使風力強勁, 風機仍然慢下來. 這是固定基座的缺點.

本周我在 Youtube 看到這位帥哥介紹他如何用推車轉向輪製作可轉向風力發電機 :

DIY Wind Turbine Build (Part 1)
DIY Wind Turbine Build (Part 2)

這讓我非常振奮, 原來利用現成的東西就可以辦到, 所以才會跑去五金行找. 果然讓我找到這款, 輪子與平台間有一個滾珠軸承, 正是要利用這個來讓風機可以隨風向改變風向, 讓風機可以全速運轉. 一個只要 50 元, 蠻便宜的 :



不過有一個問題, 發電機如果轉個幾圈的話, 線不是會絞在桿子上嗎? 難道還要用個集電環? 我看這位帥哥就直接把線拉下來而已, 似乎問題也不大. 不過買回來目前也沒時間做, 先擱著好了.

至於發電機蓋子, 打算參考下列影片, 用塑膠水管來做 :

# How i made my mini wind turbine HAWT

傍晚趁太陽還沒下山, 趕緊叫菁菁幫忙為芒果套袋, 結果兩株都沒套完, 100 張袋子全用光了, 大大出乎我預料, 沒想到兩棵芒果樹就超過 100 顆果實! 我還有四棵芒果樹呢! 只好下周再買 100 張了. 然後把同事給我的木瓜樹種在菜園南邊的甘蔗旁邊, 然後將七株小舅帶回來的紅龍果樹插枝種在附近, 下周再搭支架.

對面張家伙房的阿磬姊看到我在菜園, 跑過來聊天, 看到我過年後種的玉米, 她說可以採收了, 這時最清甜, 我本想下周再採, 她說會太老, 就幫我採了六支. 沒辦法, 只好馬上煮來當消夜, 果然真的是非常好吃, 比上週舅媽採的還嫩咧.

在煮之前我注意到其中一支玉米裡面特別肥大, 打開一看不得了, 除了正常的玉米粒外, 還長出類似皇帝豆的乳白色超大米粒 :


這該不會是基改玉米吧? 真是太奇怪了. 今天上班我特地拿來公司給同事看, 他們也嘖嘖稱奇, 非常驚訝, 從來沒看過長成這樣的玉米.


姊姊校慶園遊會

上周六 5/2 姊姊學校舉辦校慶園遊會, 導師有問家長是否要參加中午聚餐, 我早上還要去圖書館, 所以沒參加, 打算下午再去.

吃過午飯但覺頭腦混沌, 外面烈陽如炙, 好想睡個午覺, 但是已經跟姊姊說下午會去, 所以還是跟菁菁與水某搭捷運去 (本想騎機車較省, 但一看那疵牙裂嘴的大太陽, 而且那麼遠, 還是坐捷運算了).

到學校後, 園遊會第一攤就是姊姊的漫研社, 姊姊製作的兩本筆記本已經賣掉了 :


天氣這麼熱, 學生還是在台上熱舞, 真是佩服. 但園遊會攤子已賣得差不多了, 頂多就是到處都在賣的炒泡麵, 沒啥好吃, 這麼熱, 我跟菁菁就吃了三支冰棒! 好像沒啥好玩的, 就上三樓教室找姊姊, 這是她的教室布置作品-白龍  :


以及她同學畫的魔法少女 :



美術班的教室果然不一樣啊.


2015年5月2日 星期六

如何將 Javascript 的陣列轉換為 PHP 陣列

昨天在剖析 Yahoo 股市的成交明細下方的量價變化圖時 :

https://tw.stock.yahoo.com/q/ts?s=2412


檢視其原始碼發現, 這張圖的數據來自一組 Javascript 陣列 :


var data=[['99.50', 920],['99.40', 4791],['99.30', 2020],['99.20', 508],['99.10', 246],['99.00', 2645]];

只要以 "var data=" 當頭, 以 ";" 當尾, 掐頭截尾就可以取出 Javascript 的陣列數據了 :

[['99.50', 920],['99.40', 4791],['99.30', 2020],['99.20', 508],['99.10', 246],['99.00', 2645]]

然後用 str_replace() 就可以把框住價格的單引號去除 :

$data=str_replace("'", "", $data);

但是接下來該如何把這個陣列字串轉成 PHP 的陣列進行最大量價的擷取呢? 我參考下面這篇 :

# how to convert javascript array to php array

發現原來用 json_decode() 函式就能辦到了 :

$data=str_replace("'", "", $data);  //去除價格之單引號
var_dump(json_decode($data));

可見這函式並非只能解析 {"a":1, "b":2} 這樣的 JSON 格式, 連 [[1,2],[3,4]] 這樣的二維陣列也是可以的. 解碼後得到的二維陣列如下 :

array(6) { [0]=> array(2) { [0]=> float(99.5) [1]=> int(920) } [1]=> array(2) { [0]=> float(99.4) [1]=> int(4784) } [2]=> array(2) { [0]=> float(99.3) [1]=> int(2019) } [3]=> array(2) { [0]=> float(99.2) [1]=> int(508) } [4]=> array(2) { [0]=> float(99.1) [1]=> int(246) } [5]=> array(2) { [0]=> float(99) [1]=> int(2645) } }

這樣只要對此陣列排序, 就可以在固定位置 (例如第一元素) 取得當日最大量的價格了 (99.4 元, 4784 張).

我參考了下面這三篇文章的作法, 使用 PHP 的 usort() 函式來排序, 將自訂的 mysort() 代入 usort()  中達成倒序排序的目的. 但是要注意, 這三篇都是上升排序 (asc), 我把大於改成小於以得到下降排序 :

# PHP 如何排序二維陣列 [第1維:陣列,第2維:關聯式陣列]
PHP二維陣列排序
玩 PHP : 二維陣列排序

function mysort($a, $b) {
  if($a[1] == $b[1]) return 0;
  return ($a[1] < $b[1]) ? 1 : -1;  //改成小於以便下降排序
  }

$arr=json_decode($data);  //將陣列字串解碼還原為二維陣列
var_dump($arr);  //印出陣列結構
usort($arr,"mysort");  //呼叫 mysort() 作自訂排序
var_dump($arr);  //印出陣列結構

這個 usort() 函式主要是給需要特定比較規則來排序時用的,  當排序成功時傳回 true, 失敗時傳回 false. 其第一個參數是傳入要排序的陣列, 而第二個參數則是一個回呼函式, 此函式須傳回 1, 0, 或 -1, 以便控制元素之前後挪移. 它會被傳入兩個參數, 也就是要被比較的對象, 亦即待排序陣列的行元素. 此處我們想要以第二維為準作下降排序, 所以比較的對象就是 $arr 陣列的第二行元素 $a[1] 與 $b[1]. 當 $a[1] 小於 $b[1] 時傳回 1 往後挪, 最終最大的行元素就會排在最前面, 達成下降排序目的. 參考 :

# http://php.net/manual/en/function.usort.php

原始資料 :

[[99.50, 920],[99.40, 4790],[99.30, 2022],[99.20, 508],[99.10, 246],[99.00, 2645]]

經過下降排序後的結果如下 :

array(6) { [0]=> array(2) { [0]=> float(99.4) [1]=> int(4791) } [1]=> array(2) { [0]=> float(99) [1]=> int(2645) } [2]=> array(2) { [0]=> float(99.3) [1]=> int(2020) } [3]=> array(2) { [0]=> float(99.5) [1]=> int(920) } [4]=> array(2) { [0]=> float(99.2) [1]=> int(508) } [5]=> array(2) { [0]=> float(99.1) [1]=> int(246) } }

這樣固定第一個元素 $arr[0][0]=99.4 就是我要的當日最大量價了.

但很奇怪的是, Yahoo 股市長條圖上 99.4 元最大量顯示的卻是 4791 張, Why? 該不會是 Yahoo 的圖形渲染程式有 Bug 吧?

其他參考 :

# PHP eval(array_as_string) returns null
array_multisort函数的用法