python + lisp hy的新手注记1

想在python里用lisp方言hy的目的:

   1 用lisp去parse 包含 “数据+简单if控制流(代码、AST)”的配置文件,或者说用包含s-exp的.hy文件作为这类配置文件的实现(而不是用yml)

  以下引自编程珠玑徐宥的blog https://blog.youxu.info/2010/02/10/lisp-and-ai-2/

FORTRAN 基本上是围绕数组建立的,LISP 则是围绕链表实现的。通过研究下棋,几何题等 AI 问题的表示,我们的读者不难发现, AI 研究关心于符号和逻辑计算远大于数值计算,比如下棋,就很难抽象成一个基于纯数字的计算问题。 这样,只能存数字的数组就显得不适合。 当然我们可以把数组扩展一下,让这些数组元素也可以存符号。不过即使这样,数组也不能做到存储不同结构的数据。 比方说棋类中,车马炮各有各自的规则,存储这些规则需要的结构和单元大小都不一样,所以我们需要一个存储异构数据单元的模块,而不是让每个单元格的结构一样。 加上在AI 中,一些数据需要随时增加和修改的。 比如国际象棋里,兵第一步能走两步,到底部又能变成皇后等等,这就需要兵的规则能够随时修改,增加,删除和改变

当时几乎所有的研究者,把宝押在了实现一个通用的符号演算系统上,...... LISP 通过函数式编程来完成了这些演算规则的构建。

这里,需要提请读者注意的是, LISP 的全称是 LISt Processing, 即列表处理,但实际上 LISP 是由两种互相正交的哲学组合形成的, 一个是列表处理,另一个是函数式编程。 虽然在下面以后,我们会介绍 S-Expression 这样美妙的把两者无缝结合在一起的形式,但是为了清晰我们的概念,我要强调一下列表处理和函数式编程是两个正交的部分。实际上,我们完全可以用其他的不是函数的方式构建一个列表处理语言。

最最直接的通过图灵测试的方法不是让计算机和人脑一样思考,而是只要能够让计算机处理对话中用到的的单词,句子和符号

即使计算机拥有了对符号的操作能力,通过了图灵测试,它也未必是是“有智能”的。

什么语言适合人工智能的问题,就变成了“什么语言能做符号处理”。这个问题的答案,读者也都猜到了,就是 LISP。

 S-Expression。 这个 S,其实就是 Symbolic 的意思。 把程序数据都统一的当成符号。LISP 支持 meta-programming。LISP 程序可以处理,生成和修改 LISP 程序。这个特性,加上函数是一阶对象的特性,使得 LISP 远远比同时代的任何语言灵活。 

    其实,如果仅仅是“列表处理”和“函数式编程”,python都基本够了 。不对齐的数据有list dict  读json,yml也挺方便;对齐的矩阵有pandas的DataFrame。

    lisp的真正威力来自S表达式,最简洁直观地显式表达了AST,也便于灵活修改AST,这对定制DSL特别有用

           作为一种编程语言的语法,S表达式到处都是括号是挺恶心的,远不如python的缩进简洁;

           作为一种数据结构的表达   S表达式虽然比不上yml简洁,  但其实和json的到处是"" :和{}, C系列的 {} 相同,而比XML和HTML的<></>强太多了;

           但而作为代码数据合一的AST的表达,S表达式是独一无二地精炼的表示法!!

    也许AI领域 在小数据的统计学习,和大数据的NN连接主义网络角度看, 这种基于规则推导和符号演算,动态修改替换AST的符号主义AI研究风潮早已远去。可以说市场需求极度萎缩了。过时很久很久了,也没有啥前途

 

         但作为某些领域的配置文件,数据代码没法截然分开的领域(其实领域驱动设计不就是追求充血模型吗?2333),

         只要需求里有这样的交互步骤:

1领域专家编写规则(in DSL)

2系统parse规则、修改AST(in S-exp)、求值,返回函数或数值

 

那么lisp仍然有一席之地,基于S表达式的符号演算,仍然是最佳选择。

 

2 python对Unicode字型作为变量名/函数名的标识符(identifier)有限制,只能用能表示成文字的(英文、希腊文字母,中文汉字),其他符号无法作为标识符。这对基于符号的规则描述DSL是一个极大的阻碍。尤其是其他流行语言都支持无限制的unicode符号做变量和函数名的情况下(C C#  Java Clojure)

  (各种语言对Unicode字符作为函数名和变量的支持情况汇总:https://rosettacode.org/wiki/Unicode_variable_names

   这是python刻意而为的,因此只能靠其他语言来实现了https://www.python.org/dev/peps/pep-3131/  (大意是unicode太新了,python不是编译到中间语言 RTL(Register Transfer Language)的语言...blabla)

 

综合上面这两点理由的交集,显然就是hy最合适了。

 

看着hy的官方文档 http://docs.hylang.org/en/stable/language/index.html

和learnXin minute Y https://learnxinyminutes.com/docs/hy/

还有就是github的issue  https://github.com/hylang/hy/issues

但是,作为一个只看了《SICP》的lisp纯新手,实验了1天,各种不适应。

 

首先 要 用 -> 实现 形如 (->  x  (/ 2)  (np.ceil)) 这样的级联调用,一点也不容易。

都是小坑,但串联起来真要命。

 

1  构造 单参数函数  

np.ceil 必须用closure封一个函数。不能直接用(.ceil np),因为这时np已经成了第一个operand,x是要作为第二个operand了,需要->> 了,但是前面的(/ 2)还是需要->

而且用 . 也不行 。(. np method)  method只能是symbol写死 (图中注释掉的方式), 不能用输入fname解析为方法名。

要构造单操作数的函数  目前只能用python自己的getattr 方法   

  相当于

def get_f_in_np(fname):
if hasattr(np, fname):   
return np.getattr(fname)
else:
return lambda x:x

 

2  理解HyExpression

当import一个包含s-expression的hy文件,或者在py中用hy提供的read_str方法读入1段包含s表达式的字符串的时候

    input_str = "(* 2)"
    expr = hy.read_str(input_str)

此时的expr是一个符合python中AST的 HyExpression:或者HyList( 如果输入是'[(1,2)]'的话)

可以看到,得到的expr并不是纯lisp的“代码是数据,数据是代码”,或者说“code/data皆symbol”。

因为hy是把s表达式解释为python AST,所以得到的是Hy定义的Expression Symbol Int String  ... 大约和python基本数据类型对应吧。

这也就可以理解,python是一种“动态,但强类型(Strongly Typed”的语言。

而lisp 是 “动态+弱类型(Weakly Typed)”的语言  ——引自《Fluent Python》

虽然 “()”是对应 HyExpression的 , "((XXX))" ,每多一层(),expr就会多一层HyExpression([ ...]) 嵌套,

但在hy中,我们不能直接用括号对HyExpression 直接求值:

(expr  x) 

会提示 HyExpression  is not callable  其实是HyXXX 都是 not callable 的。

 

那么,可以采用绕弯的方法:利用“macro那一套:”。

回到lisp本来的概念:圆括号()表示对里面的S表达式求值(正则序,或者应用序),

而用macro的话,可以代入,但不求值。(这样,才方便对AST的修改,否则都是直接求值完了)。

 

 既然用无法用() 对HyXXX求值,那么 要从(/ 2) 生成一个fn 或者 用 ->求值 可

`反引号 表示 返回一个不求值的S表达式

~波浪号表示 展开、带入求值结果 。 只能在`反引号 内使用

然后,利用(eval  ) 对这个式子求值,就OK了。

用的是defn 和fn  并没有用defmacro 但想法和做法都是macro的,代换求值模型和基于macro的AST修改是lisp的精髓之一。

注意,必须是(eval  `()) 直接(`()) 是不行的:

 

3 函数级联调用时的comp与#* 

如果在python中,我们构造了list_fn = [f1, f2]  两个单参数的函数,

希望 在hy里用函数式的方法,得到一个新函数F(x) =  f2(f1(x)) 返回给py

这样,hy发挥了函数/算符的工厂的角色。

那么应该怎么做呢? 首先官方文档的里有comp http://docs.hylang.org/en/master/language/core.html

是这样的

 

comp

Usage: (comp f g)

Compose zero or more functions into a new function. The new function will chain the given functions together, so ((comp g f) x) is equivalent to (g (f x)). Called without arguments, comp returns identity.

=> (setv example (comp str +))
=> (example 1 2 3)
"6"

=> (setv simple (comp))
=> (simple "hello")
"hello"

注意 comp后面跟的不是 list 而是  两个单摆浮搁的函数

如果送进list_fn 应该怎么玩,整个官方文档没有说,我在issue里问,作者只给出了#*  也没说怎么用,然后就说要问去stackoverflow和maillist问

好吧,我试了试,总算搞定了。

3个注意点:

1 用 #* 就完成了对列表的解包(为什么不写进文档!)

2 要实现按list顺序依次调用,要先用python的reversed函数对list_fn进行颠倒。

3 因为我们要返回函数,所以直接() 就可以了, 不用再搞一次(fn [x] )

——哈哈,一共2行代码,居然有3个要点,前2少了任何1条,都写不出来,导致昨天卡了我很久。

现在看看,其实作为函数工厂真的比python还简洁呢。没有return,最后一个()直接就返回了。也不需要fn [x]  来代表lambda x:

 

4 没有cons  car cdr

导致我看《SICP》过来,一开始极端不适应。

而且比较坑的是,这些关键词本来之前hy一直有,但在最新版0.15 hy大幅度精简了关键词,这些本来有的东西全都给删除掉了

car   first

cdr   rest

cons 直接没有了。说是可以用list代替

好吧,其实解决了1-3,这第4倒不是太严重。但之前的一些教程,回帖,google返回的官网文档快照,都还有cons car cdr这些,导致了很大的迷惑性。

 

5 显示s-exp的字面值

如果直接print 显示的都是

如果我修改了表达式,然后想assert期望的结果,比较这个太不直观了

在文档里找了半天,才找到http://docs.hylang.org/en/master/contrib/hy_repr.html 

而且是hy下的,用法也不直观。在python下,这样用

from hy.contrib.hy_repr import hy_repr

input_rule_list = '["??" ((/ 2) ("ceil"))]'
expr_input_list = hy.read_str(input_rule_list) #HyExpression 

#do something with this expr

print(hy_repr(expr_rule_parsed))

显示结果:

 6 python的any all list comprehension

python的any all 是这么写的

any(x==True for x in [True False])

hy里  list表达式 现在是用lfor做,返回list

所以any的写法要稍微变一下

就是把 逻辑判断句"(= x True)" 放进lfor语句内部了

 

7 使用 .get 作为字典查询而不是get

https://github.com/hylang/hy/issues/915

(.get dict1 key1 default)

等效于 py里 dict1.get(key1, default)

而不带. 的get 类似于getattribute(),不支持默认值,找不到,会报错

转载于:https://www.cnblogs.com/xuanmanstein/p/9396556.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值