Что такое схема, эквивалентная распаковке кортежей?
В Python я могу сделать что-то вроде этого:
t = (1, 2)
a, b = t
... и a будет 1, а b будет равно 2. Предположим, что у меня есть список '(1 2)
на схеме. Есть ли способ сделать что-то подобное с let
? Если это имеет значение, я использую Racket.
Ответы
Ответ 1
В 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
как "кортеж", поскольку несколько значений не являются значениями первого класса в (большинстве) схемных реализаций.
Ответ 2
Идиома с голыми костями должна использовать применить с lambda, где вы бы использовали let, например:
(define t '(1 2))
(apply (lambda (a b)
;; code that would go inside let
)
t)
Преимущество заключается в том, что он работает над любой реализацией. Конечно, это можно использовать только в простых случаях, но иногда это все, что вам нужно.
Ответ 3
Общий термин для того, что вы ищете (по крайней мере, в Lisp -world), разрушает, а макрос, который его реализует, известен как 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)
Похоже, что хорошая реализация деструктурирования-привязки как макрос схемы.
Ответ 4
Я думаю, что это то, что вы ищете:
Посмотрите let-values
или let+
.
Ответ 5
Это работает в рэкете, если вы не хотите, чтобы принести в 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))
Ответ 6
Вот простой макрос 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 в 5 раз быстрее, чем Chez Scheme. Коварство гораздо медленнее, чем любая из этих схем.
Помимо простоты использования существующего регистра case-lambda
, мне нравится, как этот макрос принимает тот же синтаксис, что и списки аргументов определения функции. Я люблю простоту схемы. Я достаточно взрослый, чтобы помнить программирование Фортрана на перфокартах, где допустимый синтаксис сильно различался в зависимости от контекста. Схема должна быть лучше, чем это. Стремление подавить лилию на макросах, подобных этому, огромно. Если вы не можете оправдать изменение синтаксиса для определений функций, то не меняйте этот синтаксис и здесь. Наличие ортогональной грамматики важно.