2015年12月17日 星期四

Google App Engine 回應動態內容

文章標題是照翻原來的內文:Dynamic Image,這次試著處理聲音,發現一樣可以使用,就記錄一下,相關內容可見:
https://cloud.google.com/appengine/articles/python/serving_dynamic_images
http://www.renepedersen.com/2010/04/12/how-to-serve-images-dynamically-from-google-engines-datastore/

找一下舊文章發現上一篇類似主題竟然是七月的事:
http://yodalee.blogspot.tw/2015/07/google-app-engine-ajax-request.html
well,畢竟在陰間不好辦事,又遇到base64 decode的問題,弄了很久一直沒進度,這次放假問題解掉了,順利把資料送到後端;問題有兩個

一個是我之前把資料放在url 裡面一起送,結果base64 string 的'+'全變成space,後端python base64 module decode不出來,本來是用一種很蠢的解法,先讓資料經過data.replace(“ “, “+”),結果deploy 的時候遇到問題二:資料放在url 裡面有長度限制,應該要放在send資料裡才對,連帶這樣也沒'+'被取代的問題,詳情寫在上面那篇文內。

資料送到後端,寫入google app engine 的ndb 資料庫,這部分請參考:
https://cloud.google.com/appengine/docs/python/ndb/
很直覺,先建一個class 把該有的資料庫項目填進去,不管影像或聲音都是ndb 的BlobProperty
class RecordFile(ndb.Model):
  content = ndb.BlobProperty(indexed=False)
  date = ndb.DateTimeProperty(auto_now_add=True)
這是prototype 的關係,只記了兩個欄位。

在upload handler裡面,就可以用put()把資料寫到資料庫裡,用Key的get可以把資料再取出來:
recordFile = RecordFile(content=decoded)
retrieveKey = recordFile.put()

要怎麼把取出來的blob 塞進html 裡面?
這裡首先要寫一個Handler負責取出資料庫裡的BlobProperty,然後把binary 直接寫出來:
class GetAudio(webapp2.RequestHandler):
  def get(self):
    recordFile = retrieveKey.get()
    if recordFile.content:
      self.response.headers['Content-Type'] = 'audio/wav'
      self.response.write(recordFile.content)

再來我們要把跟圖片/聲音的request 都轉給這個handler,這裡我是假定跟/wav有關的url 都轉給它,在handler register加上這行:
app = webapp2.WSGIApplication([
('/wav', GetAudio),
], debug=True)

最後我們就能在圖片/聲音 tag 的src後面,直接寫上/wav, /img之類的網址:
<audio controls>
  <source src="/wav?key=XXX” type="audio/wav">
  Your browser does not support the audio element
</audio>

我這裡當然不是真的寫XXX,實際上的做法是{{ key }},再用JINJA2的render把資料塞進去,一樣的方法也適用圖片上,寫個/img 的handler 並把image 的blog 寫回去即可。

2015年12月14日 星期一

安裝Minecraft Shader

這次放假回家,試著在Minecraft 上面安裝Shader,這裡記一下過程與結果。

試了之後發現,原本要裝GLSL Shader MOD 現在都不用了,1.8 的Optfine 已經把Shader 選項整合進去,只要安裝Optifine 即可開啟Shader 選項
http://minecraftsix.com/optifine-hd-mod/
而且Optfine 的安裝也變簡單了,直接執行載下來的jar 檔就能安裝完成,跟以前手動把jar 檔拆開,把檔案複製進去,刪掉Meta-inf一堆步驟,不小心還會爆開要刪掉重裝的過程根本天壤之別。

裝完optfine再來就可以去載Shader 來玩了,這邊有三個shader可以載,列表打開也有一排shader任君選擇,不過我覺得看多了其實大同小異,通常還是用Sonic Ether's Unbelievable Shaders medium
http://minecraftsix.com/glsl-shaders-mod/
不過Shader 開了,為求效果通常把亮暗對比調得很高,這其實很妨礙建築跟探險,所以只限於拍照用。

另外是一個插曲,一開始裝好的時候,不管是哪個shader,打開畫面都會一片漆黑,下面出現類似:
Invalid program: final
Cannot create FrameBuffer
之類的錯誤,後來發現我沒用Optirun開,使用的顯卡是Intel 的HD Graphics 4000,可能是程式不相容不然就是顯卡不夠力,總之顯示不出來;後來用了Optirun 用筆電的nVidia GT 640M才一切正常,這是顯示結果:

世界變得好漂亮lol。

話說回來,這根本是在虐待顯示卡,用Ultra 的shader fps 都會降到 24左右,移動時畫面看得出頓點,用medium才能回到50 fps,而且以下個人意見:打minecraft 最好玩的還是徒手打造理想世界的那股衝勁,還有完成它的成就感,shader充其量只是裝飾的工具,真正強大的顯示卡,其實是你我的想像力呀。

2015年11月9日 星期一

Rust computation book chapter 5完成

如上上一篇所寫的,最近在用Rust 重寫Understanding Computation 裡面的ruby code,愈寫Rust 愈覺得這是款設計精巧的語言,很多語法都可以寫得相當精巧

目前進展比較多的是chapter 5 ultimate machine,該章節已經寫完了,大概是因為結構簡單,相比chapter 1的syntax 要建AST好寫很多,該段的code也已經衍合進master:
https://github.com/yodalee/computationbook-rust

在這裡有幾個譔寫上的技巧
enum 要加上#[allow(dead_code)]
這好像是rust 設計上的…feature,因為enum的code 只要沒用到就會判定為dead_code,但通常是測試檔沒測到,或主程式沒用到
其實這樣也有負面效果,也許你的enum是真的有廢選項

寫到這邊我的code 跟原書的ruby code 已經開始有差了
例如裡面的tape function,每呼叫一次write, move_tape_left這類的function,都是用自身的狀態回傳一個新的Tape回來
不過我在Rust,改用call by reference的寫法,大概是感覺到每次都return 一個新的物件感覺很浪費資源吧,這樣就變成兩者執行行為不同的來源Orz
之後檢討並改進XD

附上要如何執行computation book 裡面的內容,我在archlinux 上是真的弄了一段時間,首先要裝Ruby,再裝bundle
sudo pacman -S ruby
gem install bundle
在shell 設定檔內加入:
export GEM_HOME=$(ruby -e 'print Gem.user_dir')
export PATH=$GEM_HOME/bin:$PATH
接著就可以在computationbook 的家目錄,執行
bundle exec irb -I.
>>>require 'the_ultimate_machine.rb'
如果出錯,好像要執行bundle install 把有缺的套件補上,再來的指令就只能用貼的了,測試的檔案都在資料夾下的irb.txt裡面。

感覺Rust 愈寫愈有手感,這種東西果然是要練習寫才會變熟,下次來直播寫rust 好了XD

使用python 爬蟲與pdf 函式庫產生網頁pdf 檔

之前聽傳說中的jserv大神演講,發現一個有趣的東西:
http://c.learncodethehardway.org/book/
簡而言之就是…呃…自虐…應該說用常人不會走的路來學C

不過呢這東西目前來說只有html file,如果要印成一本可以看的文件,畢竟還是pdf檔比較方便,該怎麼辦呢?這時候用python 就對了。
概念很簡單,用一隻爬蟲爬過網頁,然後轉成pdf檔:
爬蟲的部分我是選用強者我同學,現在在Google Taipei大殺四方的AZ大大所寫的Creepy (https://github.com/Aitjcize/creepy),雖然好像沒在維護,不過我們要爬的頁數很少,不需要太複雜的爬蟲程式。
Html轉pdf選用pdfkit (https://pypi.python.org/pypi/pdfkit),這需要ruby的wkhtmltopdf,可以用gem install wkhtmltopdf安裝;再用pypdf2 (https://pythonhosted.org/PyPDF2/)將文件全合併起來,兩個程式寫起來40行就了結了,輕鬆寫意,內容如下:

爬網頁:
from creepy import Crawler
import pdfkit

class C_Hard_Way_Crawler(Crawler):
  def process_document(self, doc):
    if doc.status == 200:
      filename = doc.url.split('/')[-1].replace('html', 'pdf')
      print("%d %s" % (doc.status, filename))
      pdfkit.from_string(doc.text, filename)
    else:
      pass

crawler = C_Hard_Way_Crawler()
crawler.set_follow_mode(Crawler.F_SAME_HOST)
crawler.crawl('http://c.learncodethehardway.org/book/')
合併檔案:
from PyPDF2 import PdfFileMerger

names = ['index', 'preface', 'introduction']
for i in range(53):
  names.append("ex%d" % (i))

merger = PdfFileMerger()
for name in names:
  f = open("%s.pdf" % (name), 'r')
  merger.append(f, name, None, False)
  f.close()

f = open("Learn_C_the_hard_way.pdf", 'w')
merger.write(f)
f.close()

我承認我code 沒寫得很好,各種可能噴射的點,不過至少會動啦,信Python 教得永生;轉出來的pdf檔超醜的,感覺跟之前一些在網路上找的pdf風格有點像,每頁的標頭有些重複的內容應該要去掉,連結也全壞了,就…有空檢討並改進XD
pdf檔放在dropbox(過一段時間應該會失效):
https://dl.dropboxusercontent.com/u/3192346/Learn_C_the_hard_way.pdf

2015年11月6日 星期五

使用Rust 實作Understanding Computation

Understanding Compuation (http://computationbook.com/) 是一本滿有意思的書,它很純粹的介紹<計算>這件事,第一從程式的syntax、DFA、Deterministic Pushdown Automata到Turing Machine,第二部分則從Turing Machine 開始,列舉了很多的種運算模型都能達到和Turing Machine 同樣的運算能力;相反的,從Halting problem跟所有衍生的問題,它也告訴你運算就是有它的極限。

書裡的範例都附有Ruby code,全部放在作者的github 上面,讓人隨時可以測試:
https://github.com/tomstuart/computationbook

最近興起一個念頭,把這上面的code 全部用Rust 重寫一遍,順便當Rust的練習,現在還在實作一開始的syntax 部分;之前針對這部分寫了有關trait 的內容,不過後來被我刪掉了,因為我發現在Rust 裡面其實有更好的實作方式。
之前我說的是,設定trait 為base object,下轄的node impl這個trait,這樣Box<base>就可以容得下所有有實作這個trait 的struct,就像:
trait Expr {
  fn to_s(&self) -> String
}

struct Add {
  l: Box,
  r: Box,
}

impl Expr for Add {
  fn to_s(&self) -> String {
    self.l.to_string() + “ + ” + &self.r.to_string()
  }
}
但其實這樣根本寫不出來(要不就是極度麻煩),在reduce 的時候,要把Add裡面的box 換成另一個value,或是Add reduce 到一個Number,受限於Rust pointer的Owner設計,操作Box需要一層又一層的檢查,這裡就不細講那悲劇性的結果,事實上根本就沒有結果,code 根本不到能動的地步。

反思一下,多看了幾個Rust by Example,突然發現新的寫法:
註:主要是這篇rust 裡linked list 的寫法:
http://rustbyexample.com/custom_types/enum/testcase_linked_list.html
利用Rust 的enum,把相關的struct 都收進一個enum裡面,這個寫法還滿鮮的,而且跟書裡Ruby 的寫法比較不同。
用Ruby若要實作每個node 的reducible function,因為Ruby 偏向OO語言,書中是在每個struct …或class裡面實作reducible function;Rust 因為所有的種類node都由enum管理,會變成這樣寫:
pub enum Syntax {
  Add (Box, Box),
  Number (i32),
  Nil,
}

impl Syntax {
  pub fn reducible(&self) -> bool {
    match *self {
      Number(value) => false,
      Add(ref l, ref r) => true,
      Nil => false,
    }
  }
}

pub fn reduce(self) -> Option {
  match self {
    Number(value) => Some(Number(value)),
    Add(l, r) => match (*l, *r) {
      (Number(i), Number(j)) => Some(Number(i+j)),
      (_, _) => None,
    }, 
    Nil => None,
  }
}
註:其實我也不知道需不需要Nil 這個type。

我們要針對每個不同的型態去實作它們的函式,有些相對簡單,例如reducible(),只有variable 跟number兩樣會是false,其他就用 '_' 全部消掉,雖然所有型態的function 擠在同一個impl 裡面感覺很煩躁,不過好像沒更好的實作方式,這某種程度上來說也是語言設計上的限制吧?

另外上面也可以看到,在reducible跟reduce上,我們的寫法有些許不同,reducible使用的是&self當參數,它不會持有這個syntax的所有權;相反的reduce會將原本的node 替換掉,只能用self 當參數(好其實是我用&self就會出一些很詭異的錯誤,我還不知道怎麼解)
在使用上這會有點差別,reducible 可以一直呼叫,reduce 就只能呼叫一次,之後原本持有的變數就不能再用,我們必須用let將它的所有權轉給另一個變數,如下:
let a = Add(Box::new(Number(1)), Box::new(Number(2)));
println!("{}", a.reducible());
println!("{}", a.reducible());
let m = Add(Box::new(Number(4)), Box::new(Number(10)));
//println!("{}", m.reduce());
let m = m.reduce();
println!("{}", m.reducible());
上面註解的那行不能這麼做,之後m不再持有回傳值的所有權,之後呼叫m.reducible()會形成compile error。
其實這樣滿奇怪的,總覺得這樣用起來限制也太多;當然我們可以把syntax 再封裝到一個struct裡面,每次reduce就自動做一次let m = m.reduce(),使用者就無法亂用reduce()讓編譯爆掉,再研究要怎麼寫比較好囉…

我的code 都會放在這裡,雖然說現在真的沒什麼內容,現在還在實作ch1 的內容,而且感覺還沒進到Rust 的思考領域,寫的東西感覺大有問題……
https://github.com/yodalee/computationbook-rust