LINUX.ORG.RU

Замыкания по-русски

 ,


0

1

Часто, адепты ФП промывают мозги по-поводу того, что лексическое связывание позволяет легко строить «объекты и классы», вроде этого

(define f (lambda(fu) (lambda(x) (fu x x))))
(define sum (f +))
(define mult (f *))
А я че-то подумал щас, собственно при динамическом биндинге это сделать так же легко
(set 'f (fn() (fu n n)))
(set 'sum (fn(n) (let(fu +) (f))))
(set 'mult (fn(n) (let(fu *) (f))))
Никакого преимущества у калош тут нет, фактически.


Ответ на: комментарий от Aswed

Да просто наблюдение по-ходу пьесы. Мож кто не знает.

phill
() автор топика
Ответ на: комментарий от Aswed

Просто щас очень часто наблюдается такое, что все пиарят гребаные замыкания как серебрянную пулю, а копни чуть глубже — все фичи, приписываемые им (в плане выразительности имеется в виду) легко реализуются и без них. Просто объективности хочется.

phill
() автор топика

динамический биндинг чуть больше чем на половину сплошной лол и мозгосгибание

ибо динамический биндинг хуже держит локальность чем «алголоподобное связывание схемки»

ну и фунарги да.

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

динамический биндинг более «гоутушен/императивен» в отличии от лексического биндинга которых что0ли более «рекурсивен/рекурентен»

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

фунарги да

Фунарг решается банальной конвенцией именований. Это вам Стил мозги засрал в свое время, а вы как послушное стадо повторяете. б-е-е фунарги, б-е-е опасно, б-е-е хвостовая рекурсия.

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

динамический биндинг более «гоутушен/императивен»

Вы так говорите, как будто это что-то плохое:) Я, BTW? запутался уже, каждый под императивностью что-то свое подразумевает. Что в данном случае Вы имеете в виду под формулировкой «более императивен»?

phill
() автор топика

Что-то не понял. Изобрази на фунаргах классический «объект»

(define (make-object) 
  (define x)
  (values (lambda (x) (set! x)) (lambda () x)))

(define-values (set-a get-a) (make-object))

(set-a 1)
(get-a) ; => 1

(define-values (set-b get-b) (make-object))

(set-b 2)
(get-b) ; => 2
(get-a) ; => 1     
monk ★★★★★
()
Ответ на: комментарий от phill

динамический биндинг это по сути текстовое расширение а затем(после расширения) связывание расширившегося в контесте.

т.е если «макре» не предшествует локальное перекрытие выхватываем из глобального контекста , если же предшествует то захватывается абориген.

видать мало ты по наслаждался в отладке такого «марковского» языка

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

ага.

коммунизм так же легко построить - решается банальной конвенцией быть_святыми.

это тебе либералы мозги засрали , что Homo homini lupus est

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

http://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynam...

если совсем просто

лексическое связвание имя - обьект происходит «однократно»

в динамическом - есть явный уровень косвенности - каждый раз при обращении через имя происходит разыменование ( которое может разрешатся по разному в зависимости от контекста)

т.е динамическое даёт на один больше уровень косвенности .

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

происходит разыменование

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

И что значит косвенность?

phill
() автор топика
Ответ на: комментарий от monk

Для этого нужно отдельное окружение. Если Вы хотите, чтобы окружение было неявным и не доступным, как в схеме, я затрудняюсь это изобразить. Но опять же, first-clаss окружения (обычные списки, по-сути) — это более мощный инструмент.

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

связывание однократно - а вот с чем связано имя - т.е во что оно разименуется это по разному

в лексическом имя связывается при обьявление с некоторым слотом в котором хранится значение.

в динамическом имя связывается в некотором смысле с частью состояния в котором свою очередь может меняться связь в результате динамический биндинг гибче в простейших случаях однако если интенсивно использовать то в случае лексического связывания возможны(при должной дисциплине памяти) замыкания в дин_бинденге же нет (

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

в дин_бинденге же нет

В том лишь смысле, что окружение неявное/явное, само же по себе замыкание — это просто комплект функции с окружением, и это в динамическом биндинге вполне возможно. Разница лишь в том, что при лексическом окружение не доступно из-вне.

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

Для этого нужно отдельное окружение.

Я понимаю. Мне просто интересно как решается аналогичная задача.

И ещё, как решается задача типа:

(define (map func l [res null])
  (if (null? l) 
      (reverse res)
      (map func 
           (cdr l) 
           (cons (func (car l))))))


;;;; у пользователя
(define l 0)
(define (count x)
  (set! l (+ l 1))
  x)

(map count '(1 2 3)) ; => '(1 2 3), l = 3

В том смысле, что надо ли всем локальным переменным давать имена в стиле mypackage_myvariable? Или накладки не будет?

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

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

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

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

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

function(a,b){
   ...
   var y = doSomething(b)
   object.onEvent = function(x){
      doSomethingElse(a,x,y)
   }
   ...
}

Ествественно, подобного можно достичь и без замыканий, но с ними проще.

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

А что вам вообще дает здесь замыкание? Только то, что Вы забиндили в doSomethimgElse значение имени из изолированного в замыкании неймспейса? Какое это имеет отношение к событийной ориентированости?

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

Замыкания наоборот противоречат событийной модели, так как в последней важна динамика, а замыкания замораживают значения. Тут как раз динамическое связывание рулит, поскольку можно реализовать абстракцию, где переменные будут биндится динамически, по ходу исполнения, в режиме интерпретации, грубо говоря. Не случайно прерывания вызывали у Дейкстры дичайший батхерт.

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

Один из способов обрабатывать события это калбеки. Тот же node.js весь на них завязан. При таком подходе замыкания очень удобны поскольку позволяют передать данные внутрь калбека из какой-нибудь локальной функции. В Си например так сделать нельзя, поскольку локальные переменные разрушаются по выходу из функции, и если объявить функцию А внутри которой объявляется функция Б и передается куда-то как калбек, то по завершению функции А калбек уже будет не валиден.

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

1) Не противоречит. Мутабильность/иммутабельность переменных не связано с событийной ориентацией. 2) Отлаживать динамическое связывание переменных - ад и израиль. Не надо усложнять.

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

Если Вы хотите, чтобы окружение было неявным и не доступным

Но именно это и требуется от объектов. То есть сделать объекты на динамическом связывании - нельзя.

Но опять же, first-clаss окружения (обычные списки, по-сути) — это более мощный инструмент.

Но их нигде нет, зачем говорить о том, чего нет?

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

Замыкания наоборот противоречат событийной модели, так как в последней важна динамика, а замыкания замораживают значения.

Замыкания не замораживают значения.

anonymous
()

Вообще спор о динамическое связывание вс лексическое заканчивается на том, что лексическое связывание - строго выразительнее. Динамическое можно сделать на лексическом. Лексическое - нельзя на динамическом.

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

Динамическое можно сделать на лексическом. Лексическое - нельзя на динамическом.

Можешь изобразить?

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

Мощный ЯП не_нужен. Он, во-первых, не компиляется, во-вторых, куда ты будешь девать жаба-секретуток, им же тоже жрать нужно?

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

Это интроспекция, а не динамический скоп, не путай. Оно никак не связано.

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

Хотя вообще можно. Если сделать динамический скоп (поверх лексического), то можно делать set через eval. Поскольку все переменные в динамическом скопе глобальные, а локальных нет, то все ок.

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

Ты че там языком мелешь. Покажи код

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

Мутабильность/иммутабельность переменных не связано с событийной ориентацией

Мутабильность/иммутабельность к связыванию вообще никакого отношения не имеет.

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

Того. В динамическом скопе на самом деле никаких скопов нету - просто тупо все переменные глобальны. Через часик кину реализацию, поймешь. Щас занят просто.

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

Ну-ну. Ждемс. Однако, чтоб ты даром время не терял, замечу, что в вышеприведенном коде переменная val вовсе не глобальная

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

В динамическом скопе все переменные глобальные. Само понятие «локлаьной» переменной в рамках динамического скопа просто не определено

#lang racket
(require syntax/parse/define
         racket/undefined)

(define-simple-macro (dlet ([id:id e:expr] ...+) body:expr ...+)
  #:with (pre-tmp ...) (generate-temporaries #'(id ...))
  #:with (tmp ...) (generate-temporaries #'(id ...))
  (let ([pre-tmp (with-handlers ([(λ (x) #t) (λ (ex) 
                                               (set! id undefined)
                                               undefined)])
                   id)] ... 
        [tmp e] ...)
    (set! id tmp) ...
    body ...
    (set! id pre-tmp) ...))

(define-simple-macro (dlambda (arg:id ...) body:expr ...)
  #:with (tmp ...) (generate-temporaries #'(arg ...))
  (lambda (tmp ...)
    (dlet ([arg tmp] ...)
          body ...)))

(define (set arg e)
    (eval `(set! ,arg ',e)))

->

Язык: racket [выбранный].
> (compile-allow-set!-undefined #t)
> (set 'def-val (dlambda (arg) (set arg val)))
> (dlet ([val 'foo]) 
        (def-val 'a))
> a
'foo
> 
смысл глобального скопа в том, что все переменные как бы с самого начала программы существуют в глобальном неймспейсе, а let-форма просто переопределяет их внутри себя через set! (возвращая обратно в конце формы).

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

with-handlers там просто потому что попытка доступа к переменной до того как она определена в racket дает эррор, по-этому пришлось слепить костыль для обхода. Но это непринципиально.

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

Само понятие «локлаьной» переменной в рамках динамического скопа просто не определено

(let(b 1) (print b)) ; 1
(print b) ; nil

Да, мы верим, верим. ИЧСХ, не своим глазам, а твоим словам.

let-форма просто переопределяет их внутри себя через set!

Let форма — это сахарок, никакого сета там нет. Стыдно такое не знать

(print ((lambda(x) x)1))
(print (let(x 1) x))

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

Да, мы верим, верим. ИЧСХ, не своим глазам, а твоим словам.

ну это не опровергает моих слов. Еще раз - в лексическом скопе let вводит НОВУЮ переменную с тем же именем. В динамическом скопе - просто меняет значение старой. То что ты привел в пример в моей реализации тоже прекрасно работает:

Язык: racket [выбранный].
> (compile-allow-set!-undefined #t)
> (dlet ([b 1]) 
        (print b))
1
> (print b)
#<undefined>
> 

Не да, летформа - сахарок для сета (если мы говорим о динамическом скопе). Стыдно не знать ;)

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

Не да, летформа - сахарок для сета (если мы говорим о динамическом скопе). Стыдно не знать ;)

Что там изменяется? Там происходит связывание символа со значением на время жизни вызова функции.

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

Что там изменяется? Там происходит связывание символа со значением на время жизни вызова функции.

О чем и речь. При лексическом связывании создается НОВАЯ ПЕРЕМЕННАЯс тем же именем. При динамическом связывании существует лишь одина единственная переменная с данным именем. лет-форма просто меняет значение при входе в форму (при помощи сета или еще как-нибудь - это детали реализации и не важно) и возвращает при выходе. Т.о. при динамическом связывании существует лишь один единственный глобальный скоп в котором существуют сразу все переменные. Реализацию динамического скопа через лексический я тащем-то привел, она тривиальна.

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

То что ты привел в пример в моей реализации тоже прекрасно работает

То что я привел, одинаково работает при любом биндинге. Разница в том только. что если ты вернешь оттуда функцию, она вернется голая, без лексического скопа.

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

Она вернется в глобальном скопе. Просто пойми - динамический скоп глобальный скоп. Именно поэтому динамический скоп строго слабее лексического.

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

Нет. Не надо путать set и связывание. set ВСЕГДА изменяет некую структуру, представленную в физической памяти. После того, как ты изменил эту структуру, она остается неизменной. После динамического связывания же, у тебя ничего в памяти не остается.

Я понимаю, что ты хочешь сказать. На входе в функцию мы присваиваем переменной со значением undefined значение something, а на выходе опять сеттим в undefined. Но семантически — это не set, это всего лишь вариант реализации с помощью сетов. .

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

динамический скоп глобальный скоп

Динамический скоп — это не глобальный скоп. Динамический скоп существует на время вызова функции, но имена там могут быть отличными от глобальных.


(set 'a 1)
(set 'fu (fn() a))
(set 'fu2 (fn(a) (fu)))
(print (fu2 10)) ; 10
(print (fu)); 1

Глобальное a у тебя тут 1 а не 10. Скоп fu у тебя будет зависеть от контекста вызова, а контекст может быть отличным от глобального.

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

И я щас в твоем коде не могу толком разобраться, я эти извращения ракета не знаю, но я вижу одно. У тебя там реализовано все через макросы, а значит у тебя еще до рантайма все раскроется, это почти весь смысл теряет, нет там динамики, я уж не говорю, что твой dlet не first-class, а всего лишь макрос. Ты с ним далеко не уедешь. То что ты реализовал, даже жалким костылем не назовешь.

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

В динамическом скопе - просто меняет значение старой

У этой «старой», как ты ее называешь, никакого значения вообще нет, вне контекста вызова.

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

set ВСЕГДА изменяет некую структуру, представленную в физической памяти.

Так же как и dlet.

После динамического связывания же, у тебя ничего в памяти не остается.

С чего ты взял?

Но семантически — это не set

Именно что семантически динамическое связывание - банальный set. Ты никак не сможешь его отличить от set, ну просто потому что внутри динамическое связывание именно так и работает.

Но вообще разговор не о чем - речь ведь шла о том, что легко можно сделать динамик скоп из лексического (просто убрав все скопы кроме глобального), что я и продемонстрировал.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.