Что эквивалентно схеме распаковки кортежей? - PullRequest
17 голосов
/ 19 ноября 2010

В Python я могу сделать что-то вроде этого:

t = (1, 2)
a, b = t

... и a будет 1, а b будет 2. Предположим, у меня есть список '(1 2) в схеме. Есть ли способ сделать что-то подобное с let? Если это имеет значение, я использую ракетку.

Ответы [ 6 ]

26 голосов
/ 19 ноября 2010

В ракетке вы можете использовать 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 как «кортеж», так как несколько значений не являются значениями первого класса в (большинстве) реализаций схемы.

8 голосов
/ 18 сентября 2012

идиома голых костей - использовать применить с лямбда там, где вы будете использовать пусть , например:

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

Преимущество заключается в том, что он работает в любой реализации. Конечно, это можно использовать только в простых случаях, но иногда это все, что вам нужно.

7 голосов
/ 21 ноября 2010

Общий термин для того, что вы ищете (по крайней мере, в Lisp-мире): destructuring и макрос, который его реализует, известен как 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)

Похоже, что есть хорошая реализация destruuring-bind как макроса схемы.

4 голосов
/ 19 ноября 2010

Я думаю, это то, что вы ищете:

Посмотрите на let-values или let+.

0 голосов
/ 16 января 2019

Вот простой макрос 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 - 5xбыстрее чем Chez Scheme.Guile намного медленнее, чем любая из этих схем.

Помимо простоты использования существующего кода case-lambda, мне нравится, как этот макрос принимает тот же синтаксис, что и списки аргументов определения функции.Я люблю простоту схемы.Я достаточно взрослый, чтобы помнить программирование Фортрана на перфокартах, где допустимый синтаксис сильно различался в зависимости от контекста.Схема должна быть лучше, чем это.Стремление подавить лилию на макросах, подобных этому, огромно.Если вы не можете оправдать изменение синтаксиса для определений функций, то не меняйте здесь и этот синтаксис.Важно иметь ортогональную грамматику.

0 голосов
/ 08 ноября 2018

Это работает в Racket, если вы не хотите вносить зависимость match:

Из списка:

(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))
...