LINUX.ORG.RU

Деструктивное присваивание

 ,


0

2

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

(define a 1)
(define l '(1 2 3))
(define chng (lambda(x) (write (eq? x a)) (set! x 10) (write (eq? x a))))
(define chng1 (lambda(x) (write (eq? x l)) (set-car! x 10) (write (eq? x l))))

(chng a)
(newline)
(chng1 l)

#t#f
#t#t
Получается, что мы не можем деструктивно присвоить с помощью set!, присваивание происходит локально. А операции vector-set! car-set! и прочие, изменяют значения глобальных имен в аналогичных вызовах. Какого хрена?

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

Ну и нефиг. Есть же специальные каналы на фриноде (вроде), если хочешь поговорить про лисп

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

Не знаю. Просто я вижу в этом противоречие. В лиспах, где можно

(define a 1)
(define chng (lambda(x) (set x 'foo)))
(write a); foo
Все выглядит абсолютно органично. А в схеме, которая позиционирует себя, кстати, как «чистый» язык, это выглядит как грязный хак. Я чую, что эти все выкрутасы связаны с противоречиями в реализации лексических замыканий, но как именно, не пойму.

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

А нахрена мне его читать? Меня ведь интересует не сама реализация интерпретатора, а конкретно, почему так сделано? Я подозреваю, что только потому, что не сделай они этого, схема была бы настолько неповоротливым маловыразительным языком, что на ней бы просто никто не стал писать. Отсюда и эта клоунада.

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

Просто я вижу в этом противоречие

Потому что кальций надо жрать, а не план курить.

(define (make-box x)
  (lambda (message)
    (case message
      ((set!) (lambda (new-x) (set! x new-x)))
      ((get)   x))))

(define (box-ref box)
  (box 'get))

(define (box-set! box new-x)
  ((box 'set!) new-x))



(define a (make-box 1))
(define (chng x) (box-set! x 'foo))
(chng a)
(write (box-ref a)) ; foo

О май гад, Данила! Что вообще происходит? Кругом одни противоречия!

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

А нахрена мне его читать?

Чтобы избавиться от галлюцинацийпротиворечий.

Меня ведь интересует не сама реализация интерпретатора

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

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

Ты бы лучше подозревал, что наркотики до добра не доведут.

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

Меня ведь интересует не сама реализация интерпретатора, а конкретно, почему так сделано?

Потому что вызов функции не должен внезапно менять значения переданных переменных. Если я пишу (set! x1 x) (set! y (foo x)), то я точно знаю, что значение переменной x при этом не поменяется и условие (eq? x1 x) останется верным.

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

Да не, просто мне кажется это органичным.

А это уже вопрос вкуса. Common Lisp, Scheme и Picolisp предлагают три разных подхода к написанию программ.

Подход Common Lisp'а менее удобен для функционального стиля, зато удобнее для объектно-ориентированного.

Picolisp удобен для скриптов, но для него невозможен компилятор.

Scheme/Racket чуть сложнее для понимания, зато даёт больше возможностей.

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

Потому что вызов функции не должен внезапно менять значения переданных переменных. Если я пишу (set! x1 x) (set! y (foo x)), то я точно знаю, что значение переменной x при этом не поменяется и условие (eq? x1 x) останется верным.

Да, но ведь это же справедливо только для тех переменных, которые связаны в теле функции

(define destruct (lambda(x) (set! y 10)))

(define y 1)
(define x #f)
(set! x y)
(write (eq? x y))
(destruct 'anything)
(write (eq? x y))
#t#f
Деструктивные операции со свободными переменными могут приводить к тем же эффектам, насколько я понимаю.

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

примитивы передаются по значению, а коллекции по ссылке

Не совсем - всё передаётся по ссылке, но примитивы - неизменяемые величины, а те же списки - изменяемые.

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

Тоесть, да, я понимаю, что

значения переданных переменных

функция не изменит, но нет никакой гарантии, что она не изменит свободные в ее теле переменные, поэтому все равно нельзя быть уверенным, что (eq? x y)-->#t в любом месте программы.

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

Какого хрена?

Как только ты поймёшь, что такое привязки (bindings), подобные вопросы отпадут и всё должно стать на свои места.

ilammy ★★★
()

По аналогии с CL могу предположить, что set! забиндивает на локальный символ новое значение, а set-car! — берет значение (ячейку cons) и меняет ее чать. В принципе можно предположить какую-нибудь функцию set-value!, которая бы брала значение и меняла его целиком. Ну и как бы (set! some-list new-value) тоже не меняет «внешнее» значение some-list.

staseg ★★★★★
()

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

Если нет - ладно, попробуй, я не знаю, джаву какую-нибудь или что еще подобное, попроще. Питон. Потом приходи на лисп. Видимо, синтаксис мешает понимать суть, хрен знает. А может действительно курить дудки тебе надо меньше.

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

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

lovesan ★★
()

Получается, что мы не можем деструктивно присвоить с помощью set!, присваивание происходит локально. А операции vector-set! car-set! и прочие, изменяют значения глобальных имен в аналогичных вызовах.

Нет, set! происходит локально _всегда_. Можешь привести хоть один пример, когда оно происходит нелокально?

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

Да, но ведь это же справедливо только для тех переменных, которые связаны в теле функции

А только для них это и нужно.

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

функция не изменит, но нет никакой гарантии, что она не изменит свободные в ее теле переменные

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

anonymous
()

Мне известен только один императивный язык программирования, в котором вообще нет никаких ссылок — ровно так, как хочет автор.

q)a:1
q)l:1 2 3
q)chng:{0N!(x~a);x:10;0N!(x~a);}
q)chng1:{0N!(x~l);x[0]:10;0N!(x~a);}
q)chng a
1b
0b
q)chng1 l
1b
0b
anonymous
()
Ответ на: комментарий от anonimous

Все выглядит абсолютно органично. А в схеме, которая позиционирует себя, кстати, как «чистый» язык, это выглядит как грязный хак. Я чую, что эти все выкрутасы связаны с противоречиями в реализации лексических замыканий, но как именно, не пойму.

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

В Scheme всё передаётся по значению. Тебя вероятно напрягает то, что значением переменной l является не список, как таковой, а ссылка на объект-список. Но это сама суть лиспа. Список это частный случай пары (x . y), где y это ссылка на другой список. Если у нас нет типа «ссылка на пару», у нас нет лиспа.

Никакого противоречия нет. Всё логично и продумано.

Похоже ты хочешь такой set!, который делал бы изменённую копию исходного объекта и присваивал бы её передаваемой переменной. Такое можно сделать в каком-нибудь «чистом» лиспе, но это уже будет не Scheme. Да и не лисп в его исходном виде.

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

Тебя вероятно напрягает то, что значением переменной l является не список, как таковой, а ссылка на объект-список.

Ну это какие-то вялые схоластические отмазы.

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

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

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

Все и передается по значению, а не по ссылке.

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

Деструктивные операции со свободными переменными могут приводить к тем же эффектам, насколько я понимаю.

В этом случае (define y 1) и (define destruct (lambda(x) (set! y 10))) будут в одном блоке программы и изменение очевидно.

Да, если надо синтаксически именно так как ты хочешь

(define x 1)
(chng x)
x ; -> 100

Можешь определить chng как

(define-syntax-rule (chng x)
  (set! x 100))

Будет работать так, как ты хочешь, но не будет являться функцией (нельзя, например, передать в map)

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

Если нет - ладно, попробуй, я не знаю, джаву какую-нибудь

нет-нет-нет, батенька, я не хочу из-за него отписываться от тэга java! :333 Пусть перейдет на C# какой-нибудь.

stevejobs ★★★★☆
()
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от Legioner

Ничего не мешает в чистом языке иметь деструктивное присваивание, если его деструктивность вынесена в тип, см. Data.IORef в хаскеле.

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

Я почти не знаю Haskell, но сомневаюсь, что это позволяет изменять обычным образом объявленный биндинг.

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

Да, деструктивные апдейты имеют смысл только для специальных референсов (IORef, STRef, MVar, ...); биндинги - это всего лишь уравнения.

anonymous
()

Подозреваю, что пациент не понимает указатели.

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

когда-нибудь он и, скажем, Perl найдут друг друга. хотя перл не для того, чтобы задротить, а практический инструмент. хм, тогда не знаю

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

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

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

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

Не скажи. Может он ищет язык, на котором было бы комфортно программировать. У меня этот путь был Pascal -> C++ -> Common Lisp -> Racket. С учётом предпочтение ТСа, я бы ему посоветовал Tcl или Picolisp — оба «абсолютно органичные» и замыкания там при необходимости явным образом прописываются и передача по-ссылке есть.

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

Picolisp удобен для скриптов, но для него невозможен компилятор.

С чего бы? Такой же (ну ладно еще чуть более лажовый), как sbcl: что может скомплирует сразу, а что в рантайме после интерпретации.

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

С чего бы?

С того, что в любой момент любой кусок данных может использоваться как ссылка на переменную или код. Нету отдельных фаз компиляции и выполнения, каждое выражение обязано анализироваться непосредственно перед выполнением. В общем, всё равно, что делать компилятор bash'а.

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