電気通信大学の学祭である
調布祭のオープンキャンパスで
展示をしてきました。
暗号制御のリファレンス実装を持って行ったのですが
それだけでは寂しいので
景品がミニドーナツなクレーンゲームを作りました。
一個一個掴む前提で組んだのですが、
二個スタックしてゲットされる方が。
こういうモノは実地に持って行くと面白い事が起こりがちですが
これもそういった楽しい誤算の一つですね。
"->"はthreading macroといい、Hylangを特徴付ける記法の一つです。
シェルスクリプト のPipeのような感じで記述できるので、
Lisp系特有の地獄の括弧ネストを幾分見通しよくすることができます。
伝統的書き方だと
(setv *names* (with [f (open "names.txt")] (sorted (.split "," (.replace "\"" "" (.strip (.read f)))))))
これがhreading macroであれば以下のように
(setv *names* (with [f (open "names.txt")] (-> (.read f) (.strip) (.replace "\"" "") (.split ",") (sorted))))
パッと見では解釈が難しいこんな式も
(import [sh [cat grep wc]]) (wc (grep (cat "/usr/share/dict/words") "-E" "^hy") "-l")
これが
(import [sh [cat grep wc]]) => (-> (cat "/usr/share/dict/words") (grep "-E" "^hy") (wc "-l"))
hy2pyはhy環境に付属するpythonスクリプトコンバーターです。
何からの事情でPythonしかない先で使うときに活用できそうです。
(import time) (import serial) (setv ser ( serial.Serial "/dev/ttyACM0" 57600)) (defn robotTask [dist] (-> (+ "task," dist "\n") (str.encode) (ser.write))) (robotTask "up") (for [x ["right" "down" "left"]] (robotTask x) (.sleep time 6.0))
これを
$ echo sample.hy | hy2py3
でコンバートすると……こうなります
import time import serial ser = serial.Serial('/dev/ttyACM0', 57600) def robotTask(dist): return ser.write(str.encode('task,' + dist + '\n')) robotTask('up') for x in ['right', 'down', 'left']: robotTask(x) time.sleep(6.0)
巷で大人気のPythonには豊富なライブラリ群があります。
教材の類も充実してますし、会社や研究室の先行プロジェクト資産もあるでしょう。
Hylangは幸いにしてPythonとの連携機能が充実しているので
こういった先行資産を活用しながら、徐々にLisp色に染めていくというスタイルを
現実的に実行可能なヤベぇ特徴を備えています。
今回はオープンソースなロボットアームOpen Manipulator Xを
シリアル通信経由で操作する単純なアプリケーションを考えます。
当該ロボットアームはUbuntu端末からシリアル通信経由で所定の指令文を送ることで操作します。
Pythonでやると、こんな感じになります。
import serial ser = serial.Serial('/dev/ttyACM0', 57600) ser.write("task,up\n")
これをHylangで書き直してみましょう。
まず、ライブラリインポートですが、これはそのまんま。
(import serial)
変数設定
次に変数設定。
letは残念ながらない*1ので、
Hy公式のスタイルに則ってsetvで値を定義します。
文字列はダブルクオートじゃないと受け取って貰えないので注意!
(setv ser ( serial.Serial "/dev/ttyACM0" 57600))
次に書き込みです。なんも考えないと、こんな感じの実装になるかと思います。
(ser.write (str.encode "task,back\n"))
Clojure由来らしい魔界記法"スレッドマクロ"をつかうと……以下のように書き下せます。
(-> (str.encode "task,up\n") (ser.write) )
指令文本体は単体の要素として先に切り出して渡してあげてもいいので……
(-> "task,up\n" (str.encode) (ser.write) )
この例ですと旨味がないですが、"."記法を使う場合、
スレッドテイル・マクロを使い末尾にくっつけるようにして
こう書くこともできますね。
(->> "task,down\n" (.encode str) (.write ser) )
スレッドテイル方式のものを関数に切り分けましょう。
defunじゃなくてdefnです。
(defn robotTask [dist] (-> (+ "task," dist "\n") (str.encode) (ser.write))) (robotTask "up")
繰り返し処理も見通しよくなりますね。
(import time) (for [x ["right" "down" "left"]] (robotTask x) (.sleep time 6.0))
*1:欲しい時は自分で作りましょう
HyはPython上に実装されたLISP方言です。
Pythonのライブラリを活かしながら、LISP的な記述ができる強みがあります。
今回はこのHy言語をUbuntu18LTSの上に構築してみます。
Python上で動いているので、当然Pythonの環境は必要です。
今回はPython3の上に構築しました。
導入はほかのライブラリと同様pipでいけますが、標準のロード先にはないためgithubのリポジトリから導入します。
$ pip3 install git+https://github.com/hylang/hy.git
$ hy hy 0.17.0 using CPython(default) 3.7.3 on Linux => (+ 1 2) 3 => (print 'hello) hello => (defn say [hello] (print hello)) => (say 123) 123 =>