perl syntax

From
Bulat Ziganshin (2:5093/4.126)
To
Artem Chuprina (2:5054/37.63)
Date
2005-04-06T20:52:26Z
Area
RU.PERL
* Originally in RU.PERL
Приятного тебе дня и незабываемой ночи, Artem!

Wednesday April 06 2005, Artem Chuprina writes to Bulat Ziganshin:
 BZ>> оптимизация tail calls - это мелочь. главная проблема
 BZ>> неэффективности функционального стиля программирования - работа
 BZ>> со значениями вместо работы с ячейками памяти

 AC> Ну, Lisp, например, работает со значениями указателей.  Если это не
 AC> ячейки памяти...  Ну, понятно, если мы все-таки о результирующем
 AC> машинном коде, а не о том, с чем работает непосредственно программист.

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

 AC> Ocaml не смотрел, не знаю.  Собственно, лишних копирований значений
 AC> (и структур) при функциональном стиле должен избегать компилятор.
 AC> При самой тупой реализации проигрыш да, будет.

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

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

мои ощущения основаны на том, что я сейчас делаю архиватор и на тех операциях, которые я особо не оптимизировал или которые зависят больше от стандартных бибилиотек (а они тоже написаны в основном на самом Haskell), проигрыш в скорости по сравнению с RAR и 7-zip составляет обычно 2-4 раза. ну например, на архивации и деархивации без сжатия. те же операции, над улучшением реализации которых я поломал голову, например сортировка списка файлов - работают не хуже, чем у "конкурентов". и это при том, что у меня в отличии от них функция сравнения создаётся "на лету"!


 BZ>> и использование списков вместо массивов

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

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


 BZ>>>>>> а что в нём сделано с передачей параметров!!!

 DG>>>   Вам не нравятся ссылки?

 BZ>> проблема в том, что стандартные функции принимают списки/хеши в
 BZ>> развёрнутом виде. соответственно, на программисте лежит
 BZ>> удовольствие по созданию/разыменовыванию всех этих ссылок. в ruby
 BZ>> сделано лучше - там все объекты представлены ссылками и
 BZ>> использование ссылок совершенно прозрачно. ты можешь написать
 BZ>> что-то вроде:

 BZ>> x = { "a" => [1, 2, {"b"=>nil}] }

 BZ>> затем передать эту переменную в процедуру или наоборот -
 BZ>> возвратить её. доступ ко всем элементам и их изменение
 BZ>> прозводится напрямую:

 BZ>> x["c"] = x["a"][2]["b"]
 BZ>> x["a"][3] = x["a"][2].keys

 AC> x = {"a" => [1,2], "b"=>[3,4]}

наверно, нужно дополнительное разъяснение. в ruby все значения представлены ссылками, при этом все манипуляции с этими ссылками производятся неявно. скажем, в твоём примере x указывает на значение типа Hash. в этом хеше есть 4 ссылки - на строки "a" и "b", массивы [1,2] и [3,4]. x["a"] возвращает ссылку на первый массив. её можно передать в процедуру, присвоить переменной, возвратить и т.д. присваивание элементу массива или хеша вызывает процедуру "[]=", которой передаются сам массив/хеш, индекс/ключ и присваиваемое значение. например,   x["a"] = [5,6]   вызывет порцедуру  x.[]= ("a", [5,6]) которая изменит ссылку, хранящуюся в первом элементе этого хеша

 AC> С этим, в общем, согласен.  Один вопрос.  Предположим, у меня

 AC> x = {"a" => [1,2], "b"=>[3,4]}

 AC> Каким выражением (statement не интересует, нужно именно выражение) я
 AC> могу получить из этого [[1,2],[3,4]] и [1,2,3,4]?

первое очень просто - x.values, второе - x.values[0]+x.values[1], а в общем виде - x.values.inject { |sum,a| sum+a }

 AC> В перле, с его четким разделением на список/массив и ссылку на массив
 AC> (одно из весьма немногих в нем ортогональных мест), мне это понятно.
 AC> А тут?

да, это ортогонально, но сложновато для понимания (а ведь это самая базовая возможность!) и без этой сложности можно прекрасно обойтись, как обходятся питон, руби и множество других языков. я так понимаю, порблему создало то, что в пердле первоначально появились три типа переменных - $ @ % и это было очень удобно для работы только с plain lists/hashes. но при переходе к составным структурам данных и тем более классам определение типа значения по первой букве имени уже невозможно. и разработчики языка сделали здесь некоторую затычку, которая позволяла сохранить полную совместимость со старым кодом и внести минимальные изменения в язык, но при этом их решение не было ни изящным, ни удобным для понимания. то есть тут нужен был некоторый рефакторинг, а они обошлись хакингом уже существующей возможности. не зря автор Руби говорил, что его язык - это Перл5 такой, каким ему следовало бы быть :)  причём он собирается сделать и Ruby 2.0 несовместимым с Ruby 1.x - в частности потому, что некоторые принятые в последнем решения оказались неоптимальными


Bulat, mailto:bulatz-AT-fort.tatarstan.ru, ICQ: work 15872722, home 11849833

... Иногда для того, чтобы изменить свое восприятие мира,
... люди пытаются изменить сам мир
--- GoldED+/W32 1.1.2
 * Origin: Чубайс Бессмертный - повелитель Тьмы (2:5093/4.126)
SEEN-BY: 450/208 452/25 100 454/9 455/15 461/33 74 106 640 464/34 465/204
SEEN-BY: 467/24 469/125 478/44 65 550/150 5068 4600/126 4614/9 4623/56 4625/9
SEEN-BY: 4626/100 4632/10 4635/99 1024 4641/444 4642/27 48 4657/50 5001/50
SEEN-BY: 5002/76 5002 5003/34 5010/53 146 5011/13 5015/4 28 214 5019/5 5020/52
SEEN-BY: 5020/115 128 133 150 175 486 600 642 744 794 921 958 968 982 1100
SEEN-BY: 5020/1169 1212 1234 1626 1642 1653 1826 1829 1930 2044 2200 2345 2908
SEEN-BY: 5020/4400 4441 5021/2 5023/11 5024/1 73 5025/19 5030/69 195 382 436
SEEN-BY: 5030/611 920 1016 1039 1520 1688 5031/7 63 5032/11 20 5033/21 35
SEEN-BY: 5034/8 5035/38 63 5036/13 5037/21 36 5038/4 5040/33 47 5041/4 5045/7
SEEN-BY: 5045/42 5047/47 5049/6 157 5050/9 41 47 5051/35 5053/16 38 5054/1 8 9
SEEN-BY: 5054/35 36 37 45 50 66 67 81 85 5055/177 5056/16 5058/77 5059/2 9 20
SEEN-BY: 5060/90 5062/4 7 5063/41 51 5064/7 35 36 5070/26 66 948 5071/22
SEEN-BY: 5075/37 5077/70 5079/49 5083/13 5090/23 105 5093/4 27 33 57 5096/18
SEEN-BY: 5100/113 6023/1 6033/2727 6070/5 6096/10
PATH: 5093/4 5020/52 5054/1 37