LINUX.ORG.RU

common lisp, loop, bound


0

0
(let ((l (list 1 2 3)))
	   (let ((lambda-list 
		  (loop for i in l collect
		       (lambda () (print i)))))
	     (loop for lam in lambda-list 
		do (funcall lam)))) 
3 
3 
3 
NIL
(let ((l (list 1 2 3)))
	   (let ((lambda-list 
		  (loop for i in l collect
		       (let ((j i))
			 (lambda () (print j))))))
	     (loop for lam in lambda-list 
		do (funcall lam)))) 
1 
2 
3 
NIL

Собственно принимаю это как фичу, просто интересно стало, на каком этапе происходит связывание внутри тела lambda, если имеем такой результат в первом листинге?

Этот loop обычно раскрывается в примерно такую форму: (let ((i ...)) ...)

Т.е., на все итерации цикла (и на все созданные замыкания) будет одна привязка i. Во втором листинге же на каждой итерации создается новая привязка.

Таким же образом ведут себя все ЯП с замыканиями, например, javascript и c#.

PS. В CLHS не указано, как именно происходит связывание переменных цикла, но обычно привязки переменных создаются один раз на цикл.

dmitry_vk ★★★
()
Ответ на: комментарий от dmitry_vk

ясно, спасибо.

У меня ещё есть один вопрос, который я забыл написать в топике. Что делать, когда надо раскрыть список по-элементно в такой ситуации:

(defun some (&rest l)
...)

(defun more (l)
;нужно вызвать some с l
)
пока думаю примерно так -
(eval `(make-bit-generator ',agenda ',true-table ,@wires))
Чаще всего такое возникает, когда нужно передать rest-параметры и несложно обернуть это в макрос, но может есть какое-то более стандартное решение?

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от pseudo-cat

Если нужно раскрыть/разобрать список аргументов, то используется destructuring-bind. Если нужно вызвать функцию со списком аргументов, то apply.

dmitry_vk ★★★
()
Ответ на: комментарий от dmitry_vk

Таким же образом ведут себя все ЯП с замыканиями

4.2

# let funs = Array.make 3 (fun () -> ()) in
     for i = 0 to 2 do
        funs.(i) <- (fun () -> Printf.printf "%d\n" i)
     done;
     for j = 0 to 2 do
        funs.(j) ()
     done;
  ;;
0
1
2
Miguel ★★★★★
()
Ответ на: комментарий от dmitry_vk

Мой код показывает, что в более-менее нормальных языках эффектов подобных упомянутому ТС, не наблюдается.

А биндинги-шминдинги - не то, о чём требуется думать рядовому девелоперу.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> А биндинги-шминдинги - не то, о чём требуется думать рядовому девелоперу.

Монады-шмонады - это тоже не то, о чем требуется думать рядовому девелоперу.

anonymous
()
Ответ на: комментарий от anonymous

Ой, ви таки где-то увидели монады в приведённом коде на OCaml?

Miguel ★★★★★
()
Ответ на: комментарий от dmitry_vk

а как поведет себя iterate на аналогичном коде?

dolist судя по всему создает каждый новую привязку

korvin_ ★★★★★
()
Ответ на: комментарий от dmitry_vk

>Таким же образом ведут себя все ЯП с замыканиями

| lambdas |
lambdas := #(1 2 3) collect: [:each | [Transcript << each; space]].
lambdas do: [:each | each value]

"1 2 3 "

Лямбда [:each | ...] вызывается для каждого элемента и возвращает замыкания с уникальными each.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

P.S. Теперь сравните этот код на Smalltalk с вышеприведенными аналогами на Common Lisp и Ocaml ;-)

Ну уж давайте:

  let funs = Array.init 3 (fun i () -> Printf.printf "%d\n" i) in
  Array.iter (fun f -> f ()) funs

Тем, кто знает Smalltalk, будет понятнее ваш код, тем, кто OCaml, мой. А объемы примерно равны.

satanic-mechanic
()
Ответ на: комментарий от yoghurt

>P.S. Теперь сравните этот код на Smalltalk с вышеприведенными аналогами на Common Lisp и Ocaml ;-)

Ты хотел показать что на смолтоке тоже можно писать абсолютно нечитаемый код?

cvb
()
Ответ на: комментарий от cvb

Я от Смолтока знаю, в общем-то, в основном название, но код понятен идеально.

Miguel ★★★★★
()
Ответ на: комментарий от cvb

>абсолютно нечитаемый

Какое конкретно слово не понятно? Первой строчкой собираем урожай замыканий, второй - их же и вызываем. Песня, а не код!

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Немного магии... При минимальном знакомстве с языком все весьма понятно:

(def funs (map #(fn [] (println %)) '(0 1 2)))
(dorun (map #(%) funs))

Или немного по-другому, чуть ближе к вашему:

(def funs (->> '(0 1 2) (map #(fn [] (println %)))))
(dorun (->> funs (map #(%))))

satanic-mechanic
()
Ответ на: комментарий от Miguel

>Мой код показывает, что в более-менее нормальных языках эффектов подобных упомянутому ТС, не наблюдается.

Эти эффекты есть в любом нормальном ЯП. При этом подверженность цикла этим явлениям - штука независимая. Так как эти эффекты и есть суть замыкания.

dmitry_vk ★★★
()
Ответ на: комментарий от Miguel

Ну, я понимаю, что внутри ленивые значения — те же замыкания. Но вы пользуетесь именно ленивостью, не создавая замыкания явно. С таким трюком я бы мог и на clojure так же написать:

  (def funs (map println '(0 1 2)))
  (dorun funs)

Это не совсем честно.

satanic-mechanic
()
Ответ на: комментарий от dmitry_vk

Эти эффекты есть в любом нормальном ЯП.

Смотря что понимать под «эти эффекты».

При этом подверженность цикла этим явлениям - штука независимая.

Вообще, мне казалось, эффект в том и состоит, что цикл ведёт себя не так, как ожидается.

Miguel ★★★★★
()
Ответ на: комментарий от satanic-mechanic

Ну, я понимаю, что внутри ленивые значения — те же замыкания. Но вы пользуетесь именно ленивостью, не создавая замыкания явно.

Это НЕВЕРНО. Ленивость в данном случае не имеет отношения к происходящему.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Это НЕВЕРНО. Ленивость в данном случае не имеет отношения к происходящему.

Буду признателен за пояснения. Хаскеля я не знаток пока что.

satanic-mechanic
()
Ответ на: комментарий от satanic-mechanic

В игре участвует чистота языка (а не ленивость).

В данном случае, первым элементом списка является результат вычисления выражения «print 1», имеющий тип «IO ()». Этот тип означает, что такое значение есть описание некоторого ДЕЙСТВИЯ, которое, будучи выполнено, вернёт пустой результат (произведя, возможно, какие-то сайд-эффекты).

Поскольку язык чистый, в нём нет конструкций, позволяющих действительно выполнить это действие (исключая unsafe-функции, которыми пользуются от большой надобности). Можно только составлять из имеющихся действий одно большое действие - что и делает функция sequence_: из трёх действий в списке она составляет одно, которое (если его выполнить) произведёт такие же эффекты, как эти три действия по порядку.

Повторюсь: в программе нет никаких конструкций, позволяющих действительно выполнить действие (тем самым обеспечивается чистота и ссылочная прозрачность); всё, что мы делаем - это собираем одно большое действие, которое потом будет выполнено рантаймом.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

OK, понял, но суть вышеприведенной «претензии» это не снимает :) Что в прочем совсем не важно ибо больше половины темы — оффтоп.

satanic-mechanic
()
Ответ на: комментарий от satanic-mechanic

OK, понял, но суть вышеприведенной «претензии» это не снимает :)

Почему же?

Или вы считаете, что «замыкание» - это обязательно функция?

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Нет, ни считаю, «претензия» _не по сути_. Просто очень короткий код достигнут использованием возможностей, не используемых в оригинальном посте, только и всего. То есть код не один в один. Но это и не претензия вообще-то, так.

satanic-mechanic
()
Ответ на: комментарий от satanic-mechanic

common lisp, loop, bound (комментарий) - тоже.

Можно и на Хаскеле мимикрировать под оригинальный пост:

module Loop where
import Control.Monad
import Data.Array.IO
main =
    do actions <- newArray (1,3) (return ()) :: IO (IOArray Int (IO ()))
       forM [1..3] $ \i -> writeArray actions i (print i)
       putStrLn "Ready to go!"
       forM [1..3] $ \i ->
           do action <- readArray actions i
              action
       return ()
Вывод:
Ready to go!
1
2
3
Будет примерно то же самое.

Miguel ★★★★★
()
Ответ на: комментарий от yoghurt

не схватите диабет от тонны сахара =)

CL-USER 1 > (defmacro collect ((var list) &body body)
              (let ((acc (gensym "COLLECT-")))
                `(let (,acc)
                   (dolist (,var ,list (nreverse ,acc))
                     (push (progn ,@body) ,acc)))))
COLLECT

CL-USER 2 > (defvar fns (collect (x '(1 2 3)) (lambda () (print x))))
FNS

CL-USER 3 > (dolist (fn fns) (funcall fn))

1 
2 
3 
NIL

CL-USER 5 > 
korvin_ ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.