發表文章

目前顯示的是 二月, 2010的文章

注意 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"

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

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

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

用 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"); …

安裝 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 反查, 就會卡個一陣子繼續。有兩種簡單解法:
如官網所言, 重跑 mysqld, 加上 --skip-name-resolve在 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 sa…

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 展開的結果, 可以用 m…

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 可以更精確地掌握特徵,但也會因此認得較少特徵;反過來說,若想認得更多特徵,得讓內容發散,但很可能學錯特徵。若想同時學得又準又多,只好提供更多訓練資料…

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 !pythonautocmd 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) 即可。

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

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 執行程式 (當作互動式直譯器) 等功能。照著官方文件說的做就是了, 安裝的摘要如下:
download limpsudo aptitude install vim-full sbcl hyperspec rlwrap screen perlsudo ./install.sh  # 會把檔案裝到 /usr/local/limp, 並設 soft-link 指回 ~/.vim/ftplugin/lisp/ 下編輯 ~/.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" 時隨便輸入一個代名,…

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 沒有盛行?

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。

用 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__ 的問題。

覆寫 __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" 不同的結果。

超淺的 Word sense disambiguation 學習心得

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


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

參考資料
http://en.wikipedia.org/wiki/Word_sense_disambiguationhttp://en.wikipedia.org/wiki/Part-of-speech_tagging
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: SVMPOS: 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 frequencybass + fish or sea -> fishA hard example: A dog barks at a tree.bark: 吠 or 樹皮?I…

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

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

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

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

增加 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 檔。

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