2010年2月28日 星期日

注意 Python exec 的 scope

在 iPython 裡無法用 exec? 查到 exec 的文件, 卻能查到 eval、execfile。我才發現原來 Python 2 的 exec 是 statement! 如同 print, 到 Python 3 才改為 function。

若要讓 exec 執行後的結果能被其它 scope 的程式存取, 要改用「exec CODE in globals()」或「exec CODE in locals(), globals()」, 見下面的例子:

def add_hello():
    exec "def hello(): print 'hello'"
    hello()

add_hello()  # "hello"
hello()  # NameError: name 'hello' is not defined

def add_hello():
    exec "def hello(): print 'hello'" in locals(), globals()
    hello()

add_hello()  # "hello"
hello()  # "hello"

2010年2月25日 星期四

Blog 切割, 非技術文移至新 Blog

抱著隨便亂撒種子的心態種了這個 blog, 結果長沒多久儼然變成技術導向的短文集散地。有時想寫些非技術文, 卻覺得怪怪的, 寫了不知給誰看。另一方面, 偶而有非資工的朋友說他們來看我的 blog, 卻不知在寫啥。乾脆就將這兩者切開吧。想想自己去看些工程師 blog 時, 有時也沒興趣看他們的日常生活。

本 blog 英文名稱仍舊是 http://fcamel-life.blogspot.com/, 只是中文名稱改為「fcamel 技術隨手記」。Coding is my life! 所以這英文名稱絕對沒錯啦。另外新開 http://fcamel-daily.blogspot.com/, 中文名稱為「fcamel 雜記」, 歡迎舊雨新知逢場。

2010年2月23日 星期二

用 strace 和 ltrace 找出用到的 system call 和 library call

前面提到 host 沒有 call gethostbyaddr, 面惡心善的 Scott 大概是查覺我下載了原始碼, 卻沒有找出確認它的方法。於是在另一篇留言裡說可以用 strace、ltrace 或 gdb 輕易做到這事 (幸好我還沒開始試 profiler 啊...)。

strace 和 ltrace 顧名思議, 它們會列出執行期間用到的 system / library call。若不確定有興趣的函式是那個, 可以用 man page 編號來判別。比方 man gethostbyaddr 顯示被分在  section 3 下, 所以 gethostbyaddr 是 library call [*1]。

分別拿 host 和對照組 getent 試的結果, 可以看到 getent 有打開 /etc/hosts (從 strace 那看到的), 並呼叫 gethostbyaddr;而 host 卻兩者皆無。解開疑惑實乃人生一大痛快之事, 感謝 Scott 的指點。

附上參考用指令:
$ ltrace getent hosts 127.0.0.1 2>&1 | grep gethostby
gethostbyaddr("\177", 4, 2)                      = 0xb7ed0aa0
$ strace getent hosts 127.0.0.1 2>&1 | grep "/etc/hosts"
open("/etc/hosts", O_RDONLY|O_CLOEXEC)  = 3

$ strace host 127.0.0.1 2>&1 | grep "/etc/hosts"
$ ltrace host 127.0.0.1 2>&1 | grep gethostby

另外我好奇之下試了傳說中人人都會寫的 C 版 「Hello, world! 」, 結果發現 compiler 很聰明地用 puts 而非 printf。

參考程式如下:

#include <stdio.h>

int main(void)
{
    printf("hello, world!\n");
    puts("hello, world!\n");
    printf("hello, world! %d\n", 3);
    return 0;
}

執行結果:
$ ltrace ./f > /dev/null
__libc_start_main(0x80483f4, 1, 0xbf812ab4, 0x8048450, 0x8048440 
puts("hello, world!")                            = 14
puts("hello, world!\n")                          = 15
printf("hello, world! %d\n", 3)                  = 16
+++ exited (status 0) +++

接著我用 ltrace 執行 python (ltrace python -c ''), 結果 ltrace 狂噴訊息卻不會停......, 今日閒暇時間用盡這待那天有緣再來研究吧。

2010-02-25 更新

Scott 在留言裡提到也可以用 LD_PRELOAD  抽換動態載入的函式來達到同樣目的 (確認是否有呼叫 gethostbyaddr)。範例程式如下 (稍微修正 Scott 的範例讓它在我的機器能正常 compile):
$ printf '#include <stdio.h>\n#include <assert.h>\nvoid gethostbyaddr(void) { assert(0); }\n' > t.c
$ gcc -fPIC -shared t.c -o t.so
$ LD_PRELOAD=./t.so getent hosts 127.0.0.1
getent: t.c:3: gethostbyaddr: Assertion `0' failed.
Aborted
$ LD_PRELOAD=./t.so host 127.0.0.1
1.0.0.127.in-addr.arpa domain name pointer localhost.

上面的範例透過 LD_PRELOAD 讓程式改用自訂的 gethostbyaddr。如此一來, 抽換掉懷疑的函式再執行指令, 就知道是否有用到了。LD_PRELOAD 的詳細說明可參考 jserv 翻譯的 《Modifying a Dynamic Library Without Changing the Source Code / 在不更動原始程式碼的前提下,修改動態程式庫》

備註
  1. man man 可看到各 section 的含意, 摘錄如下:
    1 Executable programs or shell commands
    2 System calls (functions provided by the kernel)
    3 Library calls (functions within program libraries)
    4 Special files (usually found in /dev)
    5 File formats and conventions eg /etc/passwd
    6 Games
    7 Miscellaneous (including macro packages and conven-
    tions), e.g. man(7), groff(7)
    8 System administration commands (usually only for root)
    9 Kernel routines [Non standard]

    總共也才九節, 遊戲竟然自成一節......。

2012-01-30 更新

見 tag strace 下的新文章, 了解比較詳細的 strace 使用例子。

2010年2月22日 星期一

安裝 man pages (Linux system calls (2) and Library calls (3))

Ubuntu 裝好後, 預設沒有 system 和 library calls (如 man printf )。得另外安裝:
sudo aptitude install manpages-dev

MySQL 建立連線時會延遲一下

最近用 Python 的 MySQLdb 連線忽然會卡個五秒才完成, 一路試的結果, 發現五秒鐘全用在 new _mysql.connection。改用一般的 mysql client 試, 也會卡五秒, 才發現問題出在 MySQL server。這和之前登入 SSH 時會延遲一下是同樣狀況, 連本機沒問題, 連另一台就會出問題。

參照《How MySQL Uses DNS》的說法, 即使連 IP, MySQL server 也會查 DNS。若沒設好 IP 反查, 就會卡個一陣子繼續。有兩種簡單解法:
  1. 如官網所言, 重跑 mysqld, 加上 --skip-name-resolve
  2. 在 mysqld 執行的 server 上, 在 /etc/hosts 裡幫 client 所在的 IP 隨便加筆記錄。如 client IP 為1.2.3.4 就加上:

    1.2.3.4 myhost.org.tw myhost
    這樣就不會去問 DNS 等個五秒才放棄。
以下是較不相關的碎碎念。
man page 說設好 /etc/hosts 後應該會立即生效, 除非是程式有設 cache。我試的結果是, host、nslookup 都沒反應, 確認過 /etc/nsswitch.conf、/etc/host.conf 都說會先查 /etc/hosts 才查 DNS, 搞不清楚為啥 (也不知道到底是以 nsswitch.conf 還是以 host.conf 為準)。後來乾脆用 C network API (gethostbyaddr) 直接查, 藉此確認我有設對 /etc/hosts.conf。原以為可以秒殺, 結果寫寫一堆問題。用 C coding 真麻煩, 後來改用 Python 瞬間搞定:
import socket
print socket.gethostbyaddr('127.0.0.1')

2010-02-22 更新

Scott 說明才知道 host 和 nslookup 不會查 /etc/hosts, 將他的留言直接貼進來:
This is a common problem that newbies run into. Answer: host(1) and nslookup(1) does not call gethostbyname(3) or the IPv6 safe getnameinfo(3) but issue DNS requests directly.

What you want is getent(1) from glibc:

getent hosts 127.0.0.1
查 man host 才發現, 文中沒有提到 /etc/hosts 啊!! 而 man gethostbyname, gethostbyaddr 才有提到 /etc/host.conf 和 /etc/hosts。

2010年2月20日 星期六

Practical Common Lisp - ch3 Practical: A Simple Database

http://gigamonkeys.com/book/practical-a-simple-database.html

由於之前有學過一點 Scheme 和 scripting language, 初讀前半時覺得有些無趣, 沒有那種神兵利器的感受也沒有新鮮感 [*1]。讀到後半看到 macro 時, 開始覺得有趣, 讀完後覺得「嗯......, 還不錯啦」, 直到自己試著用 Python 想寫類似的功能時, 才驚覺「沒可能!! (請用日文發音) 這麼點簡單的小事, Python 竟然做不到, 而 CL 輕易地做到了!!」[*2]


摘要

  • CL 並不是 pure functional language, 可看到像 imperative language 的條列式寫法 (即像 C 那樣一行行寫下去, 程式也一行行地執行)。
  • 函式、變數名稱沒分大小寫。
  • Nil 表示偽值, 其餘值表示真值。CL 有預設參數, 沒傳參數的預設值為 Nil。若要區分沒傳入參數或傳入 Nil, 得在寫函式參數時指明額外的變數用來表示是否有參數傳入。像 Java、Python 同時有 False 和 null (None), 就沒這問題。很難說何者的作法較佳。
  • plist 有點像 associated list, 但聽別人說 CL 沒有 hash table, 應該還是不同東西吧。現階段也只會照範例程式用。
  • CL 的 format 相當於 C 的 printf, 只不過換用另一種外星語表示參數。
  • macro 很威, 見下文。

macro


CL 可以定義函式或 macro, 定義 macro 和定義函式語法差不多, 差別在於傳給 macro 的參數不會被執行 (evaluated), 而是當成資料交給 macro 處理。運算完的結果再當作程式來執行 [*3]。書上的範例如下:

(defmacro backwards (expr) (reverse expr))

執行範例如下:

CL-USER> (backwards ("hello, world" t format))
hello, world
NIL

若 backwards 是一般函式的話, 這個例子會 compile error, 因為 "hello, word" 不是函式。若想看 macro 展開的結果, 可以用 macroexpand-1 (最後那個是數字一):

CL-USER> (macroexpand-1 '(backwards ("hello, world" t format)))
(FORMAT T "hello, world")
T

書上最後的範例是用 macro 做出產生 SQL where 語法的函式。這個函式會產生比對欄位值的函式 (用 Lisp / Python / Ruby 的角度來看, 可當作 filter 的參數)。這和直接寫死的差別在於:
  1. 可以接受不定的參數。
  2. 只會比對給定的參數, 不需執行沒給定的參數。注意, 是「不需執行」, 不是「不用比對」。
舉例來說, 若資料表查詢欄位有 A、B、C 三者, 寫死的話不管查詢時是傳入 A、B、C 還是只傳 A, 程式至少都會做三次判斷 (是否有傳入欄位 X? 若有, 傳入的值是否符合? )。但用 macro 產生程式的話, 要幾個欄位就只會比對幾個欄位 (比方產生一個函式, 它只比對 B 的值, 不檢查有沒有傳入 A、C)。接受不定參數還有辦法用其它方式搞定, 但不執行多餘的程式, 卻是非得動態產生程式碼不可。而 macro 讓這件事變得很容易。

作者給了個很妙的評論: 用 macro 可以寫出更抽象 (也意味著更短) 的程式, 並且還能執行得更快。和 C 的 macro 差異在於, C 的 macro 是用前置處理器展開的, 它不懂語法, 容易產生很難除的錯 (應該不少人誤加分號, 結果出現一大片莫明奇妙的 compile error 訊息吧)。而 CL 的 macro 是由 compiler 展開的, 比較安全且有彈性 [*4]。讓程式處理資料, 比前置處理器展開文字有更多發揮空間。並且, 在編輯期間展開 macro 意味著不會拖慢執行速度。難怪用過的都說神!!


疑問 / 抱怨

  • 太多外星符號, 像是 「'」、「`」、「#'」、「`」、「,@」。也許習慣後就好。
  • 用 macro 會不會很難除錯? 不易維護?

備註

  1. 有寫過 C/C++/Java 的人, 初次寫 scripting language 應該會很驚呀吧。若沒感到驚呀, 表示連皮毛都沒學到, 寫得太 C/C++/Java 了。不然應該能寫得又短又好懂。
  2. 雖然 Python 可以用 exec 在執行期間執行新產生的程式碼, 仍受限於用編輯器寫程式的框架, 不易用程式處理, 挺多用個文字樣板代換關鍵的程式碼。而產生完的程式, 更難做第二次處理。而 Lisp 用 list 表示程式碼成為它的優勢, 程式碼和資料並無顯著區別, 也難怪各家程式語言學不起 macro 這招。Paul Graham 曾對此吐槽, 若其它語言想加入 macro, 大概得用相似於 Lisp 的表示方式, 只不過這樣就不是一個新的程式語言, 而是另一個 Lisp 方言 (dialect) 了。
  3. 原文寫得比較清楚:

    the Lisp compiler passes the arguments, unevaluated, to the macro code, which returns a new Lisp expression that is then evaluated in place of the original macro call. 
  4. 但 CL 是 dynamic typing, 所以.......即使 macro 展開時沒有 compile error, 不表示執行時不會有語法錯誤。

Classification (ML) 和學英文都需要大量的訓練資料

最近聽 TED 練英文發覺有趣的事。TED上的題材五花八門,講者來自世界各地,讓我提早體會到不同發音的問題。若只在單家補習班練英文,大概無法體會到世界各地的腔調。體會歸體會,這樣聽下去能否一次通殺是另一個問題。

在練習聽力的同時,我同時觀察自己怎麼理解這些發音的。這實在是很奇妙的過程,有些音明明和標準發音差了十萬八千里,但是一連串的字彙連起來,卻能立即明白整句在說什麼 (比方 "William Kamkwamba: How I harnessed the wind" );有些字彙即使講得再標準,沒有相關知識也聽不懂 (即使是母語也是如此)。看來重點是前後文和背景知識。

以往學英文時總強調要多背單字,我試過幾次都失敗,我實在不是背書的料,但我深信有需求自然會用。回顧過去失敗的經驗,我發覺用不到的字背再多次也記不起來。更何況,既然 (幾乎) 用不到,背它們做什麼?投資報酬率太低了。反過來說,常用的字一再出現,有時不用查單字也能依上下文推論出它的意思。而且還會模糊地學會它在不同情況的差異,但要仔細地描述它是什麼意思,卻又說不上來。

這樣的學習過程中,我發覺若要增加我認識的字彙,我得聽讀更多內容,才能交差地認得這些單字。像我昨天讀 "2 China Schools Said to Be Tied to Online Attacks" 學到一些單字,其中一個單字是 specialist,意思是專家。今天重聽 "Ken Robinson says schools kill creativity" 又聽到 specialist,我自然就記得了。但若這幾天或幾週內沒再碰到 specialist,我可能會忘記。我得每天花上一小時以上的時間訓練英文能力,才能持續增長我的字彙。

想到這裡,忽然發現這和 classification 認得特徵 (feature) 的方式相似。提供大量的訓練資料和它們對應的分類,learning model 自然會從訓練資料中找到重覆的特徵,分辨出那些特徵和某些分類有關或是反相關。在同樣數量的訓練資料中,若訓練資料的內容相似、特徵類型集中,model 可以更精確地掌握特徵,但也會因此認得較少特徵;反過來說,若想認得更多特徵,得讓內容發散,但很可能學錯特徵。若想同時學得又準又多,只好提供更多訓練資料。

對應回英文字彙也是如此。若我想習得準確且多量的英文字彙,只好持續投入大量時間接觸英文。雖說是理所當然的事,想通後才更甘心地花時間練英文。話說回來,不同的 learning model 用不同方式掌握分類和特徵的關係。強的 model 可以用較少的資料學得又準又多。人腦這麼強大,應當能更有效率地學英文。雖說持續投入時間接觸英文是必要的,也要不斷摸索如何學得更有效率。目前的想法是當我能用自己的話用上這些字彙時,就能記牢它們。所以要試著用這些字造句,或直接重述一遍原文。很土法煉鋼的作法,總比過去坐在那唉唉叫都不動來得好。

附帶一提,人類學習語文時會不斷用上背景知識和前後文,classification 卻缺少這兩項東西,使得這類解法侷限在特定領域,被歸類到 applied AI。我還無法想像如何將人類的背景知識 (或稱常識?) 編入電腦,但至少待電腦的運算能力和記憶體更大之後 (這是可預見的發展),電腦可以認得更廣的前後文 (現今的作法最多只看三個字彙左右),配合更大量的訓練資料,也許會有出乎意料的進展。就像電腦在解決下棋之類的益智遊戲時,並無法如人類用較少的模式掌握情況,但能用較簡單的方式配合大量運算達到更好的效果。

2010年2月19日 星期五

Python 的 Closure

仔細想想 Python 真是奇妙的語言, OOP 做半套、也摻一點 FP, 可是都不完全。 Paul Graham 在 Accumulator Generator 裡列了各種語言實作累加器的方法, 題目描述如下 (摘錄自該文):
The problem: Write a function foo that takes a number n and returns a function that takes a number i, and returns n incremented by i.
該文用 OOP 實作, 程式如下 (摘錄自該文, 並加上 object):
class foo(object):
    def __init__(self, n):
        self.n = n
    def __call__(self, i):
        self.n += i
        return self.n
我原本想到的作法是用 Closure, 寫好的版本如下:
def foo(n):
    def inc(i):
        inc.n += i
        return inc.n
    inc.n = n
    return inc


目前還不知道使用 Closure 的適當時機, 總覺得包成 class 也 OK, 只是用 Closure 似乎簡潔一些。印象中 Aethanyc 曾說 Python 的 Closure 有些弱, 目前還沒什麼感覺, 待日後遇到應用情境時再說吧。

附帶一提, 我試用了 Google Prettify Code, 還不錯用。用法參見《用google prettify code给blogspot代码着色》

在 VIM 中執行選取區塊內的程式

用 Limp 時發現可以選取區塊後直接執行, 轉念一想, 這應該可以用在任何有直譯器的語言。首先在 ~/.vimrc 內加入這兩行 (以 Python 為例):
autocmd BufRead,BufNewFile *.py map <F10> :% w !python
autocmd BufRead,BufNewFile *.py vmap f :w !python
這裡的意思是編輯副檔名為 py 的檔案時才套用後面的命令。第一行表示按 F10 就將內文全部透過 pipe 丟給 python。相當於在 shell 下這麼做:
cat MY_SCRIPT.py | python
第二行將指令對應到 visual mode (按 V, v, Ctrl+v 選取區塊), 選好區塊後按 f 會執行被選到的那幾行程式。假設選到的是第三到五行, 相當於在 shell 下這麼做:
head -5 MY_SCRIPT.py | tail -3 | python
選用 f 只是我個人習慣 (最不費力的鍵), 大家可以換成自己喜歡的鍵。其它語言只要有直譯器, 把設定中的 python 換成該直譯器 (比方 ruby), *.py 換成對應的副檔名 (比方 *.rb) 即可。

本文作法比較陽春, 希望足以應付大部份的情況。

2010年2月18日 星期四

Practical Common Lisp - ch2 Lather, Rinse, Repeat: A Tour of the REPL

http://gigamonkeys.com/book/lather-rinse-repeat-a-tour-of-the-repl.html

摘要

  • Common Lisp 不像其它語言由少數人或組織推動, 只有由 ANSI 制定標準, 大家再各自實作功能。有點像 W3C 定了 HTML 4, 但各家瀏覽器各做各的, 雖然有共通語法, 但共通語法也可能有不一致的行為, 另外又有各家自訂的語法, 相容問題較為嚴重。如同 Plumm 所言, 或許這就是 CL 紅不起來的主因吧。
  • SBCL 是 CMUCL的延伸實作, 可以編成 native code 在 Linux 和 OS X 上執行, 並支援 Unicode。上篇提到的 PL benchmark, 也是用 SBCL 測。
  • 作者建議用 Lisp in a Box 和 SLIME (Emacs mode for CL) , 但身為一名 VIM 的愛好者, 這部份我就跳過啦 ( 其實是我不習慣頻繁地按 Ctrl 和 Alt )。

SBCL + VIM


這篇所言, VIM plugin Limp 是 CL + VIM 的最佳選擇, 提供 code completion、將 VIM 內的程式貼到 SBCL 以及切換到 SBCL 執行程式 (當作互動式直譯器) 等功能。照著官方文件說的做就是了, 安裝的摘要如下:
  1. download limp
  2. sudo aptitude install vim-full sbcl hyperspec rlwrap screen perl
  3. sudo ./install.sh  # 會把檔案裝到 /usr/local/limp, 並設 soft-link 指回 ~/.vim/ftplugin/lisp/ 下
  4. 編輯 ~/.vimrc 加上


    filetype plugin on


    autocmd BufRead,BufNewFile *.lisp so ~/.vim/ftplugin/lisp/limp.vim
官方文件沒提到第四步, 這兩個寫法的用意都是讓 vim 在開啟 *.lisp 檔時會載入 ~/.vim/ftplugin/lisp/limp.vim。

成功的話按 F12 就能在 VIM 和 SBCL 之間切換。按 F12 時會問一些問題, 在 "Name the Lisp" 時隨便輸入一個代名, 剩下按 ENTER 即可。我用的情況怪怪的, 有時連線後看到「Booting ...」就沒反應, 但實際上 SBCL 有跑起來。這時可以按 Ctrl + C 切斷連線, 再按 F12 重連一次。待看到「Connect to [boot new]:」時別急著按 ENTER, 改按 TAB 列出已啟動的 SBCL, 這樣就能連上剛啟動的 SBCL。

初步試用, 覺得下面這個流程挺好用的:
  1. 用 V 選起要執行的區塊
  2. 按 \ec 執行
  3. 按 F12 切到 SBCL 看結果

疑問

  • 常聽人說用 Emacs 寫 scripting language 很方便, Emacs 甚至自訂了一套 Lisp 方言。究竟 Limp 差 SLIME 多少呢? 

Ubuntu 上重設時區 (time zone)

指令:
dpkg-reconfigure tzdata

像這種零碎的指令不知存在那裡較好, 以前是存在 wiki, 
結果重覆記第二次時才發現之前已查過了。記在 bbs 上又很亂, 不易搜尋。

用 rlwrap 幫任何命令列工具加上 code completion、command history

用慣 shell 後, 用命令列工具時總想按個 tab 就補完指令, 按上下就能搜尋過去打過的指令。遺憾的是, 不是每個命令列工具都支援這個功能。沒關係, 你的心聲 rlwrap  (readline wrapper) 聽到了!!

在 Ubuntu 下安裝 rlwrap 的方法是.....沒錯, 和你猜的一樣:
sudo aptitude install rlwrap
啟動方式是:
rlwrap ARGUMENT COMMAND
以 sqlite3 為例, 啟動方式是:
rlwrap -a -r sqlite3
-a 表示不管原本的程式是否有用 readline, 都改用 rlwrap。 -r 表示記下所有打過和螢幕上出現的字彙。如此一來執行 「select * from my_table;」之後, 可以直接按 tab 補完 column name 或 table name 。

另外可以編輯 /etc/rlwrap/sqlite3 存入關鍵字 (如 select、where、limit), 這樣每次執行指令時都能自動補完這些字。檔案格式是一行一個字。rlwrap 會到 /etc/rlwrap/COMMAND 找預設關鍵字, 也可另外用 -f FILE 指定關鍵字。

Practical Common Lisp - ch1 Introduction: Why Lisp?

打算寫下每章摘要, 看看這樣有無幫助學習

http://gigamonkeys.com/book/introduction-why-lisp.html

摘要

  • Motto: The programmable programming language. 要啥新功能就自己加吧, 不用擔心核心沒有。
  • Lisp 是可以被編譯成 native code 的, 這意味著它可以跑得更快 (相較於用 interpreter 執行)。
  • 用 Lisp 可以寫得更快 (作者舉了他老爸和他自己幾個例子)。
  • Scheme 和 Common Lisp 不同。前者較純易於用作教學, 但較不跨平台, 較難用來寫產品 (solve real world problem); 後者則相反, 擁有成熟的資源, 包含 GUI 函式庫、multi-thread、socket 等。

疑問

  • CL 可以跑得多快? 依這裡的數據來看, 是 C 的三倍慢, 似乎挺快的 ( Python3 是 43 倍慢)。不知實際應用時如何?
  • CL 的函式庫是否齊全? 主流語言 (C/C++, Java, Python, Ruby, Perl, PHP, Java Script 等) 有龐大成熟的函式庫, 量多又穩。
  • CL 的入門門檻?
  • 若 CL 真的如此強大, 也不需擔心上述問題, 為啥 CL 沒有盛行?

2010年2月10日 星期三

GROUP BY "half an hour" in MySQL

今天忽然有這需求, google 了 "group by half hour" 還真的有人問一樣的問題, 不過沒啥好解答。自己試出還過得去的解法, 就順手記一下吧。

假設表格 record 有個欄位 created_time, 型別為 timestamp。若要統計 2010-02-10 一整天每半小時產生的的資料量, SQL 可以這麼寫:
SELECT HOUR(created_time) AS h, FLOOR(MINUTE(created_time) / 30) AS v, COUNT(*) FROM record WHERE AND created_time >= '2010-02-10' AND created_time < FROM_DAYS(TO_DAYS('2010-02-10') + 1) GROUP BY h, v;
MySQL 有許多計算時間的函式, 像 hour 會取出小時, 但沒有取出半小時或「每十分鐘」的函式。但可以自己用分鐘除以 30, 再套 FLOOR (下界) 將時間轉為 0, 1。

2010年2月9日 星期二

用 is 來比對 None

最近看到別人 coding, 才發覺自己有養成許多好習慣卻沒自覺。有機會應該一則則寫在這裡, 那天想有系統地整理時, 才方便彙整。

這篇提到 Python None 的特性。結論是判斷 object a 是否為 None 時, 記得用 「a is None」, 不然會有下列潛在問題:

  • a == None: 覆寫 a.__eq__ 可以得到不同結果。
  • not a: 無法區分 False、0、[] (或任何 __len__ 傳回 0 的物件) 以及 None。
另外注意函式預設傳回 None。

有個類似的守則, 比對 singleton 時記得用 is 別用 ==, 原因應該一樣, 避免覆寫 __eq__ 的問題。

2010年2月7日 星期日

覆寫 __eq__ 時記得要一併覆寫 __ne__

在 Python 裡, 使用 == 和 != 時, 會呼叫不同的函式:
a == b # call a.__eq__
a != b # call a.__ne__
所以, 有可能發生 a== b 為 True, a != b 也為 True 的奇怪情況。這和 Java 只要 (只能) 覆寫 equals() 不同。

若想避免這種情形, 可以選擇覆寫 __eq__, 另外在 __ne__ 裡呼叫 __eq__:
class A(object):
    def __eq__(self, other):
        # Write your code here

    def __ne__(self, other):
        return not self == other
      
若只覆寫 __eq__ 忘了覆寫 __ne__, 之後會很難除錯, 有時 client code 使用 "not a == b" 會得到和 "a != b" 不同的結果。

2010年2月2日 星期二

超淺的 Word sense disambiguation 學習心得

( 這篇是幾個月前寫的, 補貼上來備份資料, 內文可能有誤, 請以原始資料為準 )


最近面試的人是 NLP 背景, 就簡單地惡補一下相關知識,
Wikipedia 永遠是你的好老師! 只是長期這樣眼睛很累

參考資料

WSD (word sense disambiguation) is harder than POS.
  • accuracy 70% vs. 95%
  • WSD is harder to tag. Humans are hard to know all senses of a word.

Both areas are dominated by machine learning methods.
  • WSD: SVM
  • POS: HMM
近年來 ML / Statistics 四處亂踢館啊, 還滿好奇為啥 POS 和 WSD 這麼相像,
Wikipedia 上卻說 WSD 用 classifier, 而 POS tagging 卻用 HMM 這類有考慮文字順序的 model。

Two kinds of approaches to WSD

  • Deep approach:
    • code human knowledge into computer-readable format.
    • it's very hard to use in practice.
  • Shallow approach:
    • a statistical way.
    • it works most times; when facing ambiguity, use window to reduce ambiguity.
Example
  • bass: low frequency or a kind of fish.
    • bass can be distinguished by counting word co-occurrence.
    • bass + sound -> low frequency
    • bass + fish or sea -> fish
  • A hard example: A dog barks at a tree.
    • bark: 吠 or 樹皮?
    • If we use window size <= 2, it's related to dog.

Misc
  • A naive approach (simply match POS by the most possible one) achieves 90%. 這樣的成果還挺令人無言的, 一開始最多就只有 10% 進步空間。


登入 SSH 時會延遲一下的解法

最近不知為啥 ssh 登入伺服器時, 出現 login 後會延遲一下才出現 passwd。還有兩台機器用 ssh 互連時, 會卡一陣子才登入 (我有用 public/private key)。由於我透過 ssh 來做 hg push / pull, 這個延遲問題對我來說還挺傷的, 每次和伺服器同步 Mercurial 資料就要頓一下。

執行  ssh -v 發現輸出這行後就停住了:
debug1: SSH2_MSG_SERVICE_ACCEPT received
google 後發現這篇提到這可能是 DNS 查不到 IP 的 domain name 造成的, 所以我試了一下:

  • ssh localhost # 立即登入
  • ssh MY_IP # 即使從 localhost 連也要等一會兒
  • host MY_IP  # 要等一會兒
所以我想應該是 DNS 的問題吧, 於是照該篇說的:
  1. sudo echo 'UseDNS no' >> /etc/ssh/sshd_config
  2. sudo /etc/init.d/ssh restart
問題就解決了。附帶一提, restart 後原本的 ssh connection 不會斷線, 可以安心地 restart。

2010年2月1日 星期一

增加 andLinux 的 partition 空間

我用的是 andLinux beta2 KDE, 預設裝好 andLinux 後, 整個根目錄有 4G, 可用空間剩 2G。隨便裝些玩具空間就用完了。只好擴大 partition。

google 一下, 找到這篇 ExpandingRoot 介紹如何擴充 coLinux 的大小, 也適用於 andLinux 。裡面列了許多作法, 我試過「TopoResize」和「 Toporesize - Command line tool」, 兩者都沒效, 還搞爛了 image 檔, 只好重灌 andLinux (也只是移除軟體, 重裝軟體而已啦, 雖說資料就飛了)。

後來照著「The simplest way to enlarge the root partition」做, 就弄成功了。原理很簡單, 就是下載一個別人作好的 image 檔, 掛上它, 然後把整個 / 複製到新的 partition 上。再關機修改設定, 將 root 指到新的 image 檔。

詳細做法如下:
  1. 下載別人作好的 vdi 檔。我下載 10G 那個檔, 為此還砍了和 andLinux 同槽的遊戲, 嗚.....。
  2. 用 7-zip 解開檔檔, 放到 Program Files\andLinux\Drives\ 下, 我將檔名改成 base_10G.vdi。
  3. 編輯 Program Files\andLinux\settings.txt, 加入這行:
    cobd3=Drives\base_10G.vdi
  4. 開啟 andLinux
  5. mkdir /mnt/test && mount /dev/cobd3 /mnt/test
  6. cp -ax / /mnt/test && umount /mnt/test
  7. 關閉 andLinux
  8. 編輯 Program Files\andLinux\settings.txt, 將 cobd3 和 cobd0 對調, 即:
    cobd0=Drives\base_10G.vdi
    cobd3=Drives\base.vdi
  9. 重開 andLinux, 搞定收工。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...