在 Python 中,我可以这样做:
t = (1, 2)
a, b = t
...a 为 1,b 为 2。假设我'(1 2)
在 Scheme 中有一个列表。有什么办法可以做类似的事情let
吗?如果它有所作为,我正在使用 Racket。
在 Python 中,我可以这样做:
t = (1, 2)
a, b = t
...a 为 1,b 为 2。假设我'(1 2)
在 Scheme 中有一个列表。有什么办法可以做类似的事情let
吗?如果它有所作为,我正在使用 Racket。
在球拍中,您可以使用match
,
(define t (list 1 2))
(match [(list a b) (+ a b)])
和相关的事情,如match-define
:
(match-define (list a b) (list 1 2))
(match-let ([(list a b) t]) (+ a b))
这适用于列表、向量、结构等。对于多个值,您可以使用define-values
:
(define (t) (values 1 2))
(define-values (a b) (t))
或let-values
。但请注意,我不能将其定义t
为“元组”,因为在(大多数)方案实现中,多个值不是一流的值。
一个简单的习惯用法是在您使用let的地方使用apply和lambda,例如:
(define t '(1 2))
(apply (lambda (a b)
;; code that would go inside let
)
t)
优点是它适用于任何实现。当然这只能用于简单的情况,但有时这就是你所需要的。
您正在寻找的通用术语(至少在 Lisp 世界中)是解构,实现它的宏称为destructuring-bind。在 Common Lisp 中,它是这样工作的:
(destructuring-bind (a b c) '(1 2 3)
(list a b c)) ;; (1 2 3)
它也适用于嵌套的多个“级别”:
(destructuring-bind (a (b c) d) '(1 (2 3) 4)
(list a b c d)) ;; (1 2 3 4)
看起来解构绑定作为方案宏有一个很好的实现。
这是一个简单的destructuring-bind
宏,用于带有case-lambda
(例如 Racket 或 Chez Scheme)的方案:
(define-syntax bind
(syntax-rules ()
((_ arg pat def body)
(apply
(case-lambda
[pat body]
[x def] )
arg ))))
这是促使我编写此宏的示例。将默认值放在正文之前可以使代码可读:
(define (permutations l)
;
(define (psub j k y)
;
(define (join a b)
(bind a (ah . at) b
(join at (cons ah b)) ))
;
(define (prec a b z)
(bind b (bh . bt) z
(prec (cons bh a) bt
(psub (cons bh j) (join a bt) z) )))
;
(if (null? k)
(cons (reverse j) y)
(prec (list) k y) ))
;
(psub (list) (reverse l) (list)) )
以下是在各种方案上计算长度为 9 的排列的基准:
0m0.211s Chez Scheme
0m0.273s Bigloo
0m0.403s Chicken
0m0.598s Racket
翻译成 GHC Haskell 比 Chez Scheme 快 5 倍。Guile 比这些方案中的任何一个都慢得多。
除了易于利用现有case-lambda
代码之外,我喜欢这个宏如何接受与函数定义参数列表完全相同的语法。我喜欢简单的方案。我已经足够大,可以记住在穿孔卡片上编程 Fortran,其中允许的语法随上下文变化很大。Scheme应该比这更好。像这样在宏上建立百合花的冲动是压倒性的。如果您也不能证明更改函数定义的语法是合理的,那么也不要在此处更改该语法。拥有正交语法很重要。
如果您不想引入match
依赖项,这在 Racket 中有效:
从列表中:
(let-values ([(a b c) (apply values '(1 2 3))])
(+ a b c))
或直接来自值表达式:
(let-values ([(a b c) (values 1 2 3)])
(+ a b c))