什么是Scheme中元组解包的等效方式?

23
在Python中,我可以像这样做:
t = (1, 2)
a, b = t

...那么 a 将是 1,b 将是 2。假设我有一个列表 '(1 2) 在 Scheme 中。是否有任何方法可以使用 let 来实现类似的操作?如果有不同,请注意,我正在使用 Racket。

6个回答

29
在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 相关

(match-let ([(list a b) t]) (+ a b))

这适用于列表、向量、结构等等。对于多个值,您可以使用define-values

(define (t) (values 1 2))
(define-values (a b) (t))

或者let-values。但请注意,我无法将t定义为“元组”,因为在(大多数)Scheme实现中,多个值不是一等值。


这个可以工作,但我正在寻找使用 let 的东西,而这个是用 defines 定义的。我想我可以编写一个宏,将这样的定义插入到 local 中。 - Jason Baker
2
好的,有match-let(附有一个示例),但是简单的match也可以。 (您的问题使它看起来像您想要定义。)此外,您始终可以在本地作用域中使用定义。 - Eli Barzilay
第一个例子不运行(并且在我的脑海中也没有意义)。 - cosarara97
你应该使用 racket 语言。点击我提供的链接查看参考文档。 - Eli Barzilay

13

一个简单的惯用语是在你需要使用 let 的地方使用applylambda,例如:

(define t '(1 2))
(apply (lambda (a b)
          ;; code that would go inside let
        )
        t)

这种方法的优点是它可以在任何实现上运行。当然,这只能用于简单的情况,但有时这就是你所需要的。


8

你所寻找的通用术语(至少在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)

看起来有一个很好的scheme宏,实现了destructuring-bind。

这里提供了更多信息。


4

谢谢您发布这个!唯一的问题是let-values并不完全符合我想要的功能,而且我似乎无法让需要使用let+的库正常工作。话虽如此,这个“面向Python程序员的Scheme”网站肯定会很有用。 - Jason Baker
好的,至少你有一个很酷的新网站可以挖掘,以便在遇到其他问题时使用。看一眼它,希望你能找到如何设置你的环境来使用 let+ 的方法。干杯! - icyrock.com

2

这是一个简单的destructuring-bind宏,适用于具有case-lambda(例如Racket或Chez Scheme)的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 比任何一个 Scheme 实现都要慢得多。

除了易于利用现有的 case-lambda 代码之外,我喜欢这个宏接受与函数定义参数列表完全相同的语法。我喜欢 Scheme 的简洁性。我已经老到足以记得在打孔卡上编程 Fortran,那时允许的语法随上下文而变化。Scheme 应该比那更好。对于像这样的宏进行改进的冲动是不可抑制的。如果您不能证明在函数定义中改变语法也是必要的,那么在这里也不要改变语法。拥有正交的语法很重要。


2
如果您不想引入“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))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接