1个不稳定版本
使用旧的Rust 2015
0.1.0 | 2018年9月14日 |
---|
#39 在 #lisp
165KB
6K SLoC
Lumen
Lumen是一个非常小巧、自托管的Lua和JavaScript的Lisp。它提供灵活的编译环境,具有可扩展的读取器、宏和可扩展的特殊形式,但在其他方面尽可能接近目标运行时环境。您可以在已安装Node.js、Lua或LuaJIT的机器上运行 bin/lumen
来开始使用。
简介
Lumen中的每段代码都是一个表达式,表达式可以计算为给定的值。Lumen有几种类型的表达式可以自我计算
> 17
17
> -5e4
-5000
> true
true
> false
false
字符串用引号括起来,可以包含换行符。特殊字符使用反斜杠转义
> "hi there"
"hi there"
> "one
two"
"one\ntwo"
> "a\"b"
"a\"b"
nil
代表空无,它不会被打印在命令行上
> nil
>
注释以 ;
开头,并一直延续到该行的末尾
> 7 ; everyone's favourite
7
列表包含其他值,并通过在括号中包含表达式来书写。通过将操作符放置在列表表达式的开头来调用操作符,并且可以使用 list
操作符来构造列表值
> (+ 10 2)
12
> (abs -10)
10
> (/ 128 2 2 2)
16
> (list 1 2 3 4)
(1 2 3 4)
列表可以包含通过其位置识别的值,以及通过列表中的名称识别的值
> (list 1 2 3)
(1 2 3)
> (list 1 2 a: 10 b: 20)
(1 2 b: 20 a: 10)
请注意,通过名称识别的值,即键,不会按任何特定的顺序出现。
可以使用 at
操作符从列表中获取位置值,使用 get
操作符获取键
> (at (list 1 2 3) 1)
2
> (get (list a: 10 b: 20) "b")
20
对于值为 true
的键的快捷方式,看起来像这样,称为标志
> (list :yes)
(yes: true)
变量
使用 define
和 define-global
声明变量。使用 define
声明的变量可以在同一作用域中后续表达式中任何地方使用,而 define-global
使它们全局可用。
> (define-global zzz "ho")
> zzz
"ho"
> (do (define x 10) (+ x 9))
19
do
计算多个表达式,并且自身计算为最后一个表达式的值。
使用 let
引入有限作用域的变量
> (let (x 10 y 20)
(+ x y))
30
> (let x 41 (+ x 1))
42
> (let x 1
(let x 2
(print x))
(print x))
2
1
您可以看到,let
接受名称和值的列表,称为绑定,或者它可以与单个绑定一起使用。绑定之后可以跟多个表达式,这类似于 do
> (let (x 10 y 20)
(print x)
(+ x y))
10
30
> (let x 9
(print "hi")
(let y (+ x 1) y))
hi
10
赋值
可以使用 set
更新变量和列表值,它计算为它更新的值
> (let x 10
(set x 15))
15
> (let x 10
(set x 20)
(+ x 5))
25
> (let a (list 1 2 3)
(set (at a 1) "b")
a)
(1 "b" 3)
> (let a (list foo: 17)
(set (get a "foo") 19)
a)
(foo: 19)
条件语句
条件评估使用 if
表达式。一个 if
表达式的值是条件评估为 true
的分支的值。
> (if true 10 20)
10
> (if false 10 20)
20
> (+ (if false 10 20) 5)
25
if
表达式可以有任意数量的分支。
> (if true 10)
10
> (if false 10)
>
> (if false 1 false 2 false 3 true 10)
10
> (if false 1 false 2 false 3 10)
10
> (if true 9 (do (print 10) 11))
9
> (if false 9 (do (print 10) 11))
10
11
比较值使用 =
操作符。
> (= 10 10)
true
> (if (= 10 10) "yes")
"yes"
> (if (= 10 "no") "yes")
>
列表是有唯一标识的值,因此包含相同值的两个独立列表不是同一个。
> (= (list 1 2 3) (list 1 2 3))
false
函数
Lumen 中的函数是值,就像数字和字符串一样。以 fn
开头的表达式评估为函数。
> (fn () 10)
function
可以通过将函数放在列表表达式的开头来调用函数。出现在 fn
名称之后的列表标识了函数的参数。
> (fn (a) (+ a 10))
function
> ((fn (a) (+ a 10)) 20)
30
> ((fn () 42))
42
> ((fn (a b) (+ a b)) 10 20)
30
因为函数是值,所以我们可以用变量来命名它们。调用由变量命名的函数时,适用相同的规则。
> (let f (fn () 42)
(f))
42
> (let plus (fn (a b) (+ a b))
(plus 10 20))
30
定义函数的最常见快捷方式是在以下方式中使用 define
和 define-global
。
> (define-global f (n)
(* n 10))
> (f 3)
30
> (do (define f (n) (* n 10))
(print (f 3))
(print (f 4))
(f 2.5))
30
40
25
函数的参数列表可以包含位置参数和键参数,其中键的值是绑定名称。
> (let f (fn (a b: my-b) (+ a my-b))
(f 13 b: 2))
15
如果键的值为 true
,则使用与键相同的名称来绑定参数值。这使得定义带有标志的命名参数变得容易。
> (let f (fn (a b: true) (+ a b))
(f 1 b: 2))
3
> (let f (fn (a :b) (+ a b)) ; use a flag
(f 10 b: 20))
30
Lumen 中的参数总是可选的,那些没有提供参数的参数其值为 nil
。
> (let f (fn (a) a)
(f))
>
> (let f (fn (:b) (if (= b nil) 10 20))
(f a: 99))
10
函数可以通过指定单个参数而不是列表,或者使用 rest
键来接受可变数量的参数。
> (let f (fn xs (last xs))
(f 1 2 3))
3
> (let f (fn (a rest: as) (+ a (last as)))
(f 10 11 12 13))
23
解构
变量可以绑定到列表中的特定位置或键的值。
> (let ((a b c) (list 1 2 3))
b)
2
> ((fn ((a b c)) c) (list 1 2 3))
3
> (let ((a b: my-b) (list 1 b: 2))
my-b)
2
> ((fn ((a b: my-b)) (list a my-b)) (list 1 b: 2))
(1 2)
> (let ((a :b) (list 1 b: 2))
b)
2
rest
键与解构一起使用,就像在函数参数中使用一样,它绑定列表的其余部分。
> (let ((a rest: as) (list 1 2 3))
(list a as))
(1 (2 3))
> (let ((a :rest) (list 1 2 3))
(list a rest))
(1 (2 3))
迭代
Lumen 有几种迭代机制。最简单的是 while
循环。
> (let i 3
(while (> i 0)
(print (dec i))))
2
1
0
从 0 迭代到 N 的简写是 for
。
> (for i 3
(print i))
0
1
2
可以使用 each
来枚举列表的键和值。
> (each (k v) (list 1 2 a: 10 b: 20)
(print (cat k " " v)))
1 1
2 2
b 20
a 10
> (each v (list 1 2 a: 10 b: 20) ; values only
(print v))
1
2
20
10
> (each (k (a b)) ; destructuring
(list (list 10 20) bar: (list "a" "b"))
(print (cat k " " a " " b)))
1 10 20
bar a b
each
会以任意顺序绑定键和值。如果您只想获取列表的位置值,您可以使用 step
按顺序枚举它们。
> (step x (list 1 2 3)
(print x))
1
2
3
> (step (a b) (list (list 1 2) (list 10 20)) ; destructuring
(print a)
(print b))
1
2
10
20
引号
可以使用 quote
操作符防止表达式被评估。
> (quote (1 2 3))
(1 2 3)
评估为自身的表达式不受引号的影响。
> (quote 10)
10
> (quote false)
false
引号名称和字符串会产生评估为引号表达式的字符串。
> (quote a)
"a"
> (quote "hereanother")
"\"hereanother\""
> (quote "two\nlines")
"\"two\\nlines\""
这同样适用于列表中的表达式。
> (quote (a b c))
("a" "b" "c")
> (quote (1 2 b: baz z: "frob"))
(1 2 b: "baz" z: "\"frob\"")
引号的简写是使用单个引号。
> '17
17
> '(1 2 3)
(1 2 3)
> '(a b c)
("a" "b" "c")
编写许多字符串的另一种方式是简单地引号它们的名称。
> 'a
"a"
> (let x '(a: 10 b: 20)
(get x 'a))
10
当您想引号表达式的某些部分,但想评估其他部分时,请使用 quasiquote
和 unquote
。
> (let x 10
(quasiquote (1 5 (unquote x))))
(1 5 10)
准引号的简写是 `
,而 ,
是 unquote
的简写。
> (let x 10 `(1 5 ,x))
(1 5 10)
取消引号表达式的一种不同方法是使用 unquote-splicing
,它将嵌套列表中的值放入外层列表中。
> (let a '(1 2 3)
(quasiquote (9 8 (unquote-splicing a))))
(9 8 1 2 3)
unquote-splicing
的简写是 ,@
。
> (let a '(1 2 3)
`(9 8 ,@a))
(9 8 1 2 3)
宏
宏允许您编写在评估之前操作表达式的函数。宏接受表达式作为参数并返回一个表达式。
> (define-macro when (condition rest: body)
`(if ,condition (do ,@body)))
(macro: function)
> (when true
(print 'hi)
(+ 10 20))
hi
30
致谢
Lumen 是与 Daniel Gackle 共同构思和设计的,许多修复和改进由 Shawn Presser 贡献。
依赖项
~240KB