LINUX.ORG.RU

Чистота. Что-то тут не так.

 , ,


0

2

Любопытная аналогия между ФП функциями, и математическими дробями.

Кагда мы пишем, например две дроби, например, для простоты, 1/2 и 1/2, мы абстрагируемся от оъектов, от которых мы их берем. Например, опять же для простоты, возьмем листы бумаги. Совершенно непонятно, например, идет ли здесь речь о разных листах или об одном? Одинаковые листы это или разные. Отсюда следует, что мы не можем просто так сложить эти дроби, не конкретизировав, сначала, о чем речь.

Для ФП, такой конкретизацией является общий контекст замыканий - в простом случае - глобальная область видимости. Пока все норм. Идем дальше.

Реальные приложения, это как известно, не застывшие раз и на всегда глыбы (даже глыбы иногда ломаются, да), а динамика. Значит, то что было когда то а=foo, станет a=bar. Программе все равно придется взаимодействовать с внешним миром.

Если у нас есть присваивания, мы поступим очень просто - перезапишем значение a в глобале.

Что же тогда становится с иммутабельныи замыканиями? Насколько я понимаю, единственный выход тут, полностью скопировать их и перезаписать все имена a. А что же тогда делать со старыми замыканиями? Выкинуть на помойку? Понятно, что если нам необходим возврат к старому состоянию, да, это нужно. И мы, тащемта, при таком подходе, всегда, вообще, можем вернуться к любому состоянию, это cool, никто не спорит. Но в остальных то случаях, мы получаем громадный перерасход памяти, и больше них-я?



Последнее исправление: anonimous (всего исправлений: 2)
Ответ на: комментарий от anonymous

да ладно, что печального-то? сюда же поржать ходят

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

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

А то, что действительно не нужно и до чего больше не добраться очистит GC.

Да, я это уже понял по ходу треда:), буду дальше копать, спасибо.

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

Я хотел бы поговорить с тобой о дробях. Например берем рациональные числа a и b, делим одно на другое и снова получаем рациональное. Есть много систем, где есть замкнутая операция «деление». К тому же у этих систем есть некая «иерархия», так что если целое число поделишь на рациональное, снова будет рациональное. Проблем нет.

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

Проблем нет, потому что ты действуешь в рамках системы, которая обеспечивает тебе соответствие. Вслучае Haskell-like фп это достигается type-system, и явным указанием типов. Это не отменяет самой сути.

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

Странно, что ты не знал про GC и лез в какое-то ФП. Хотя и не совсем и правило, но листы, которые какбе атрибут этого вашего ФП, часто делают через cons-ячейки. Работа с такими листами без GC - жопа.

Кстати, а как реализованы листы в Хаскелле?

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

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

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

да ладно, что печального-то? сюда же поржать ходят

Лично меня как-то не тянет ржать над убогими. Мне их искренне жаль. Плакать хочется.

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

Тогда переформулируй более ясно: почему мы не можем их сложить? И с какой точки зрения ты видишь тут проблему: программирования или математики?

С точки зрения математики берется некоторое множество, задается алгебра итд... Тогда, определенно, надо знать, как делать эти операции и какими свойствами они обладают.

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

Ну и где тут ещё проблема, стоящая целого треда? )

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

эх, если плакать над всеми тупыми убогими обмудками, то в России жить вообще невыносимо станет, а смех - это хорошая анестезия души

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

Ты так и проржешь всю жизнь, ничего не поняв.

Ого, вот тебе заявления! Значит ты правда считаешь всех несогласных с тобой персональными врагами! Лучше поговори со мной ещё чуток, может полегчает

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

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

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

Представь что у нас есть список [a b c], где a — мутабельная строка, b — мутабельная мапа с мутабельных строк на мутабельные строки, c — мутабельный список чисел. Если мы хотим эту мутабельную лапшу куда-нибудь безопасно передать, её надо скопировать целиком. Для передачи аналогичной иммутабельной структуры будет безопасно передать ссылку на неё.

Другой пример, тот же список [a b c] с теми же типами данных. Если они мутабельны, то для создания безопасного списка [a b c d] придётся скопировать целиком a, b и c. Если данные иммутабельны, то производные структуры данных весьма дёшевы, так как можно использовать деревья с ссылками.

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

Прочти, пожалуйста, сообщение, на которое я ответил

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

Сравни динамическую типизацию и статическую

Разница между статической типизацией и динамической, не в том, в чем ты ее видишь. С точки зрения ваших слонов и бегемотов, динамическая типизация сделает то же самое, что и статическая, только в рантайме. Преимущества статики в другом: она найдет ощибки на этапе проверки типов, т.е. сразу. Концептуальной разницы тут нет.

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

Всё неправда.

Если мы хотим эту мутабельную лапшу куда-нибудь безопасно передать, её надо скопировать целиком.

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

Другой пример, тот же список [a b c] с теми же типами данных. Если они мутабельны, то для создания безопасного списка [a b c d] придётся скопировать целиком a, b и c. Если данные иммутабельны, то производные структуры данных весьма дёшевы, так как можно использовать деревья с ссылками.

Откуда такие выводы? Наоборот, в иммутабельный ты ничего не добавишь

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

Наоборот, в иммутабельный ты ничего не добавишь

http://doc.akka.io/docs/akka/1.3.1/_images/clojure-trees.png

Если у нас есть списки [a b c d] и [a b c], то в памяти будут храниться a, b, c, d и легковесные дескрипторы двух списков.

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

А также, чтобы дальнейшие изменения у нас не влияли на то, что увидят у них.

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

Если у нас есть списки [a b c d] и [a b c], то в памяти будут храниться a, b, c, d и легковесные дескрипторы двух списков.

Как это связано с иммутабельностью? Если объект нельзя поменять (это и значит иммутабельный, так?), то для того, чтобы что-то туда добавить, надо создать целиком новый объект.

У тебя же, чтобы создать из списка [a b c d] список [b c d], нужно только взять хвост первого, а не создавать список заново. Так же можно изменять значение головы не создавая список заново. Здесь и не пахнет неизменяемостью

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

то для того, чтобы что-то туда добавить, надо создать целиком новый объект.

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

У тебя же, чтобы создать из списка [a b c d] список [b c d], нужно только взять хвост первого, а не создавать список заново. Так же можно изменять значение головы не создавая список заново. Здесь и не пахнет неизменяемостью

Списки неизменяемые. [a b c d] и [b c d] разные списки, которым повезло иметь некоторые одинаковые данные, которые (т. к. данные неизменяемые) можно безопасно представить компактно, ссылками на них.

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

Списки неизменяемые

Может в кложуре так. В CL cons ячейке можно менять хвост и голову

(setf (car a) b)
(setf (cdr a) b)

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

Я так и не понял как у вас там в кожуре. Я тут некомпетентен. В CL при получении списка без головы не создается ничего (нет никакого нового объекта вообще):

(time (let ((a '(1 2 3 4 5 5 ))) (loop repeat 100000000 do (cdr a))))

Evaluation took:
  0.144 seconds of real time
  0.145701 seconds of total run time (0.145701 user, 0.000000 system)
  101.39% CPU
  605,378,790 processor cycles
  0 bytes consed
  
NIL

Списки неизменяемые. [a b c d] и [b c d] разные списки, которым повезло иметь некоторые одинаковые данные,

Я выбрал плохой пример. Не буду говорить за кожуру, но есть и мутабельные списки:

(time (loop with a = (list 1 2 3 4  54) repeat 10000 do (setf (car a) 3) finally (return a)))

Evaluation took:
  0.000 seconds of real time
  0.000015 seconds of total run time (0.000015 user, 0.000000 system)
  100.00% CPU
  68,148 processor cycles
  0 bytes consed
  
(3 2 3 4 54)

А вот создание нового:

(time (loop with a = (list 1 2 3 4  54) with b = nil repeat 10000000 do (setq b (cons 3 (cdr a))) finally (return b)))

Evaluation took:
  0.239 seconds of real time
  0.208020 seconds of total run time (0.128701 user, 0.079319 system)
  [ Run times consist of 0.049 seconds GC time, and 0.160 seconds non-GC time. ]
  87.03% CPU
  1,010,625,308 processor cycles
  2 page faults
  160,006,144 bytes consed
  
(3 2 3 4 54)

Это всё к тому, что я не знаю, что у вас называется списком. У меня это вот:

http://en.wikipedia.org/wiki/Cons

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

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

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

Данные одни и те же, просто списки указывают на разные cons-ячейки:

    [a b c d]  [b c d]
        +        +
        |        |
    +---v--+     |
    | a | +|     |
    +---+-|+     |
          |  +---v--+
          +--> b | +|
             +---+-|+
                   |   +---+--+
                   +---> c | +|
                       +---+-|+
                             |    +---+--+
                             +----> d |  |
                                  +---+--+
theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от PolarFox

можно безопасно представить компактно, ссылками на них.

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

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

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

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

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

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

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

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

И да, я выбрал плохой пример.

Лучше так: можно ли из списка '(1 2 3 4) сделать '(5 2 3 4) ничего не конся? Если да, то список (и cons-ячейка) мутабельный(ые).

Пример:

(let ((a (list 1 2 3 4))) (setf (car a) 5)) ; не консит
(let ((a (list 1 2 3 4))) (cons 5 (cdr a))) ; консит

Тут мне говорят, что списки иммутабельные в кожуре. Но про кожуру я не знаю, но в том же CL это не так.

Далее следует утверждение (PolarFox'а):

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

В CL листы мутабельны. И тем не менее твоя картиночка к нему тоже применима. Я не вижу связи между изменяемостью и созданием производных структур/ссылками на какую-то часть объекта

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

Если в той картинке поменять конс-ячейку, которая используется в обоих списках, то изменятся оба списка. Да, производные объекты с использованием общих структур данных в памяти можно создавать и в случае мутабельных объектов, но они могут обладать непредсказуемым/нежелательным поведением.

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

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

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

Cons, хуёнс... В этом шизоидном треде консам не место. Здесь надо про Church pairs тереть.

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

Это я и имел в виду

Тогда с чем ты споришь? Контексты нужны как типы. У тебя в конкретном контексте будет присутствовать объект, которым ты оперируешь. В примере ТСа, с дробями, ты будешь производить операции с ними в контексте замыкания, как раз, чтобы не скрестить ежа с ужом. Абстрагироваться от контекста полностью, ты не можешь.

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

Кроме того, в CL можно и так:

(let* ((a (list 1 2 3 4)) (b (cdr a))) (setf (car b) 10) a)

(1 10 3 4)

Для этих случаев есть copy-list или copy-tree. Но касательно императивных языков, нет смысла быть адептом какого-то подхода, и тот, и тот имеет свои плюсы и минусы

Так, а с чего мы начали? Что-то про безопасную передачу ссылок. Надо перечитать всё

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

Ах, да!

Вот твоё утверждение:

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

Напомни мне, я уже доказал, что неизменяемость тут фиолетова?

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

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

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

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

Он тебя покусал или как?

Контексты нужны как типы.

Не распарсил.

Для начала, я спросил его, о чём речь: программировании или математике (ведь любит ещё в математику соваться)?

Если мы о программировании, то что такое «контекст замыкания»? Мы говорим о сложении чисел или о замыканиях? А если у меня нет замыкания?

int add (int a, int b) {return a+b;}

Если вся речь о том, что для сложения нужно знать типы, то это не секрет. Может для ТС'а это и секрет, но мне как-то не хочется спорить на эту тему.

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

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

Тут не хватает только звука фанфар и барабанной дроби.

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

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

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

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

Says who?

Замыкание (англ. closure) в программировании — функция, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции и не в качестве её параметров (а в окружающем коде). Говоря другим языком, замыкание — функция, которая ссылается на свободные переменные в своём контексте.

Отыщи тут замыкание:

double :: Num a => a -> a double = (*) 2

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

Я сказал, спорить не желаю. Если ты передал изменяемые объект в функцию, которая его меняет, и хочешь чтобы он остался прежним - ты ССЗБ. Надо было копировать.

лапочешь

Слушай, лапочка, я ебу, что ты подразумеваешь под безопасностью? Если мне, бля, НАДО поменять этот объект.

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

Основная область применения изменяемых объектов в кложуре — оптимизация вычислений внутри одной функции, чтобы много объектов, сразу кидаемых в топку GC, не создавать.

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

Замыкание в ФП - это неизменяемая структура. Функция, которая возвращается из замыкания, всегда ссылается на свой собственный скоп. Поэтому, при вызове ее из другой функции, она всегда будет вычислять только переменные, из своего контекста. В этом и заключается безопасность замыкания, откуда бы ты эту функцию не вызвал, она всегда будет обращаться к своим собственным именам. Если же ты, имеешь возможность изменять ее контекст из вне, ты не можешь быть уверен в том, что изнутри другой функции она не будет изменена. Поэтому безопасным вариантом тут будет скопировать весь контекст функции вместе с ней самой, и передать. Так ты всегда будешь уверен, что она возвратит тебе то, что ожидается.

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

Если мне, бля, НАДО поменять этот объект.

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

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