|
ru.perl- RU.PERL ---------------------------------------------------------------------- From : Bulat Ziganshin 2:5093/4.126 06 Apr 2005 20:52:27 To : Artem Chuprina Subject : perl syntax -------------------------------------------------------------------------------- Wednesday April 06 2005, Artem Chuprina writes to Bulat Ziganshin: BZ>> оптимизация tail calls - это мелочь. главная проблема BZ>> неэффективности функционального стиля программирования - работа BZ>> со значениями вместо работы с ячейками памяти AC> Hу, Lisp, например, работает со значениями указателей. Если это не AC> ячейки памяти... Hу, понятно, если мы все-таки о результирующем 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) Вернуться к списку тем, сортированных по: возрастание даты уменьшение даты тема автор
Архивное /ru.perl/3340425467e4.html, оценка из 5, голосов 10
|