DarkMatter in Cyberspace
  • Home
  • Categories
  • Tags
  • Archives

Hy Notes


Install

Hy 是一个编译为 Python 的 Lisp 方言, 类似于 Clojure 之于 JVM。 目前 conda 4.5.12 上的 Hy 版本为 0.9.12,只支持 Python 2.7, 所以需要用 pip 直接从 github 上安装:

$ conda create -n hylang python=3.6
$ . activate hylang
$ pip install git+https://github.com/hylang/hy.git
$ hy
hy 0.15.0+64.ga42e17a using CPython(default) 3.6.8 on Linux
=> (print "hello world")
hello world

升级方法是再次运行 pip install git+https://github.com/hylang/hy.git, 不需要先卸载已经安装的包,参考 pip: pulling updates from remote git repository.

更好的方法是在 IPython console 中使用 Hy (借助 Jupyter kernel Calysto/calysto_hy):

conda create -n piphy python=3.6
. activate piphy
pip install jupyter
pip install git+https://github.com/ekaschalk/jedhy.git
pip install git+https://github.com/Calysto/calysto_hy.git
python -m calysto_hy install --sys-prefix
ipython console --kernel calysto_hy
>>> (-> "abc" (.split "b") (. [1]) (print))

spy 模式

Hy 的 spy 模式打印出转换后的 Python代码再执行:

$ hy --spy
=> (setv result (- (/ (+ 1 3 88) 2) 8))
result = (1 + 3 + 88) / 2 - 8
None

=> (setv r2 (- (/ 92 2) 8))
r2 = 92 / 2 - 8
None

=> (+ result r2)
result + r2

76.0

基本语法

字符串必须使用双引号,但可以包含回车符。 单引号表示 quote,即阻止求值。

常用数据类型写法:

$ hy --spy
=> (setv list1 [1 2 3]) ; this is a list
list1 = [1, 2, 3]
None

=> (setv dict1 {:a 1 :b 2}) ; this is a dict
from hy import HyKeyword
dict1 = {HyKeyword('a'): 1, HyKeyword('b'): 2}
None

=> (setv set1 #{1 2 3}) ; this is a set
set1 = {1, 2, 3}
None

访问对象的属性(缺点是属性名称只能写死):

=> (. df1 shape)
df1.shape

(10980, 3)

访问 DataFrame 某列:

=> (. probes ["fullDesc"]) ; or (get probes "fullDesc")
probes['fullDesc']

0    1000611-SYGD-01-01-01
1    1000612-SYGD-01-01-02
2    1000613-SYGD-01-01-03
3    1000614-SYGD-01-01-04
4    1000615-SYGD-01-01-05

访问复合属性的一个元素:

=> (. df loc [(> (. df [0]) 2)])
df.loc[df[0] > 2]

管道操作(更多细节可参考 dsnote "Pipe Operator in Functional Programming Languages"):

=> (-> probes (. ["fullDesc"]) (.head))
probes['fullDesc'].head()

0    1000611-SYGD-01-01-01
1    1000612-SYGD-01-01-02
2    1000613-SYGD-01-01-03
3    1000614-SYGD-01-01-04
4    1000615-SYGD-01-01-05

slice 相关

在 Hy 中使用 Pandas loc, iloc 中括号 + 冒号 语法时可参考如下转换:

df.iloc[3:, ::2] 等价于 (. df iloc [(, (slice 3 None) (slice None None 2))])

df.loc[:, "time"] 等价于 (. df loc [(, (slice None) "time")])

df.iloc[2, 0] 等价于 (. df iloc [(, 2 0)])

df[['time', 'value']] 等价于 (. df [["time" "value"]]) 或者 (get df ["time" "value"])

df.loc[:, 'xiaoshi': 'value'] 等价于 (. df loc [(, (slice None) (slice "xiaoshi" "value"))])

df.loc[df.index > 2, :] 等价于 (. df loc [(, (> (. df index) 2) (slice None))]) 或者 (. df loc [(> (. df index) 2)] [(slice None)])

转换规则包括:

  • 用 [(, a b)] 或者 [a] [b] 代替 [a, b];

  • 用 slice 函数代替冒号,可在 IPython 中通过 slice? 查询此函数 API;

  • 用 slice(None) 代替单独的冒号;

  • 去掉 list 中的逗号;

编译为 pyc

$ cat << EOF > demo.hy
(setv people [{:name "Alice" :age 20}
              {:name "Bob" :age 25}
              {:name "Charlie" :age 50}
              {:name "Dave" :age 5}])

(defn display-people [people filter]
  (for [person people] (if (filter person) (print (:name person)))))

(display-people people (fn [person] (< (:age person) 25)))

(defmacro infix [code]
  (quasiquote ((unquote (get code 1))
               (unquote (get code 0))
               (unquote (get code 2)))))

(print (infix (3 * 5)))
EOF

$ hy demo.hy
$ hyc demo.hy

关于上面代码中宏定义相关的函数参考 3.20 Quasiquoting: quasiquote, unquote, and unquote-splicing.

反编译 pyc 文件的方法见 dsnote "Encrypt Python Source Codes" 的 "反编译 pyc" 一节。

Editor Setup

vim

默认配置下 vim 不识别 .hy 类型文件,为了让 vim 插件在 hy 文件中工作,有以下两种方法:

  • 定义新的文件类型,要求插件对其进行渲染:优点是定义准确, 缺点是对某些插件无效;

  • 将新文件类型映射到已有的某种主流文件类型:优点是适用范围广, 缺点是必须要有一种非常类似的主流文件类型,而且渲染是按主流语言的, 对于新语言来说不是特别准确;

新类型方法

在 $MYVIMRC 中添加:

" Rainbow Parenthesis
au BufRead,BufNewFile *.hy  setfiletype hy
let g:rainbow_active = 1
let g:rainbow_conf = {
\ 'ctermfgs': ['yellow', 'red', 'white', 'green', 'lightblue', 'lightred', 'lightgreen'],
\ 'operators': '_,_',
\ 'parentheses': ['start=/(/ end=/)/ fold', 'start=/\[/ end=/\]/ fold', 'start=/{/ end=/}/ fold'],
\ 'separately': {
\   '*': {},
\   'tex': {
\     'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/'],
\   },
\   'hy': {
\     'ctermfgs': ['yellow', 'red', 'white', 'green', 'lightblue', 'lightred', 'darkgreen'],
\   },
\   'vim': {
\     'parentheses': ['start=/(/ end=/)/', 'start=/\[/ end=/\]/', 'start=/{/ end=/}/ fold', 'start=/(/ end=/)/ containedin=vimFuncBody', 'start=/\[/ end=/\]/ containedin=vimFuncBody', 'start=/{/ end=/}/ fold containedin=vimFuncBody'],
\   },
\   'html': {
\     'parentheses': ['start=/\v\<((area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)[ >])@!\z([-_:a-zA-Z0-9]+)(\s+[-_:a-zA-Z0-9]+(\=("[^"]*"|'."'".'[^'."'".']*'."'".'|[^ '."'".'"><=`]*))?)*\>/ end=#</\z1># fold'],
\   },
\   'css': 0,
\ }
\}
Plug 'luochen1990/rainbow'

其中文件类型定义参考 :h new-filetype.

ctermfgs 表示 Console 中 vim 的颜色定义(gvim 的颜色在 guifgs 中定义), 颜色名称取值来自 :h cterm-colors.

Automatic closing brackets

在 $MYVIMRC 中添加:

" automatic closing brackets
inoremap " ""<left>
inoremap ' ''<left>
inoremap ( ()<left>
inoremap [ []<left>
inoremap { {}<left>
inoremap {<CR> {<CR>}<ESC>O
inoremap {;<CR> {<CR>};<ESC>O

映射类型方法

有些插件(例如 tpope/vim-sexp-mappings-for-regular-people) 用上面的方法指定类型后依然不生效,可能是插件内部对文件类型做了限制, 这时只能使用文件类型映射,将要处理的类型“伪装”成与它类似的另一个类型, 对于 hy 文件来说映射为 clojure 类型比较好,实现方法是在 $MYVIMRC 中添加:

autocmd BufRead,BufNewFile *.hy set filetype=clojure
Plug 'tpope/vim-repeat'
Plug 'guns/vim-sexp'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-sexp-mappings-for-regular-people'

这个插件的具体使用方法见 dsnote "Use vim as Clojure Development Environment", 另外由于它已经包含了括号自动补全,所以上面关于自动补全的配置就不需要了。

上面的 rainbow 插件仍然需要安装。

assoc 特性

Hy 的 assoc 与 Lisp, Clojure 中的 assoc 不同,(assoc foo bar baz) 被翻译成 foo[bar] = baz,直接改变 foo 本身,而不是保持 foo 不变返回一个新值。 造成这种区别的根本原因在于 Python 不坚持数据的 immutable, 关于这个函数行为的讨论见 issue 238, Consider making Hy models immutable, 以及 API doc 中 assoc 的 note.

目前的实现虽然不够 immutalbe, 但与 Python 的兼容性比较好, 尤其在数据分析中,如果为一个 DataFrame 添加一列,必须重新生成一个新的 DataFrame, 会导致内存消耗大幅增加。

Ref:

  • API 列表

  • Style Guide

  • Neural Networks using Keras on Rescale with Hy

  • More on Hy and why I think it is a big deal

  • languages-that-compile-to-python

  • A mile Hy - My experience with lispy Python



Published

Jan 15, 2019

Last Updated

Jan 23, 2020

Category

Tech

Tags

  • hylang 1
  • lisp 1
  • python 136

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor