Кажется, я не могу сосредоточиться на call / cc в Scheme - PullRequest
6 голосов
/ 14 марта 2011

У кого-нибудь есть хорошее руководство о том, как это работает?Было бы неплохо что-то с наглядными пособиями, все гиды, с которыми я сталкивался, как будто говорят то же самое, что мне нужно по-новому взглянуть на это.

Ответы [ 5 ]

11 голосов
/ 15 марта 2011

Вот схема, которая была оставлена ​​на доске нашей лаборатории CS.Итак, вы собираетесь принести несколько яблок, и вы берете продолжение, прежде чем начать.Вы бродите по лесу, собирая яблоки, когда в конце вы наносите свое продолжение на яблоки.Внезапно вы попадаете в то место, где вы были до того, как вы пошли в лес, за исключением всех ваших яблок .

call/cc

(display
  (call/cc (lambda (k)
             (begin
               (call-with-forest
                 (lambda (f)
                   (k (collect-apples f))))
               (get-eaten-by-a-bear)))))

=> some apples (and you're not eaten by a bear)

Я думаю, что БарМицва и похороненное золото, возможно, были замешаны.

5 голосов
/ 14 марта 2011

Взгляните на продолжение части PLAI - это очень "практично" ориентированный ", и он использует" черную дыру "визуализации для продолжения, которые могут помочь вам понять это.

0 голосов
/ 10 апреля 2012

Никогда не нравится визуальное представление call / cc, так как я не могу отразить это обратно в коде (да, плохое воображение);)

В любом случае, я думаю, что легче начать не с call / cc, а с call / ec (продолжение escape), если вы уже знакомы с исключениями на других языках.

Вот некоторый код, который должен оценить значение:

(lambda (x) (/ 1 x))

Что если x будет равен '0'? На других языках мы можем выбросить исключение, а как насчет схемы? Мы тоже можем его бросить!

(lambda (x) (call/ec (cont) 
 (if (= x 0) (cont "Oh noes!") (/ 1 x))))

call / ec (как и call / cc) работает здесь как "try". В императивных языках вы можете легко выпрыгнуть из функции, просто возвращая значение или выбрасывая исключение. В функционале вы не можете выпрыгнуть, вы должны что-то оценить. И вызов / * приходит на помощь. Что он делает, он представляет выражение в «call / ec» как функцию (в моем случае это называется «cont») с одним аргументом. Когда эта функция вызывается, она заменяет ВЕСЬ вызов / * своим аргументом.

Итак, когда (cont "Oh noes!") заменяет (call/ec (cont) (if (= x 0) (cont "Oh noes!") (/ 1 x))) на "Oh noes!" строку.

call / cc и call / ec почти равны друг другу, за исключением того, что их проще реализовать. Он позволяет только подпрыгивать, в то время как cc может быть спрыгнут снаружи.

0 голосов
/ 31 марта 2012

Я обнаружил, что это помогает визуализировать стек вызовов. При оценке выражения отслеживайте стек вызовов на каждом шаге. (См., Например, http://4.flowsnake.org/archives/602) Поначалу это может быть не интуитивно понятно, поскольку в большинстве языков стек вызовов неявный; вы не можете манипулировать им напрямую.

Теперь представьте продолжение как функцию, которая сохраняет стек вызовов. Когда эта функция вызывается (со значением X), она восстанавливает сохраненный стек вызовов, а затем передает ему X.

0 голосов
/ 15 марта 2011

В обучающем вызове нет ярлыков / cc. Прочитайте главы в Языке программирования схем или Научите себя схеме в дни Fixnum .

...