Буду ли я развивать хорошие / плохие привычки из-за ленивых оценок? - PullRequest
10 голосов
/ 23 сентября 2010

Я хочу изучать функциональное программирование на Haskell или F #.

Существуют ли какие-либо привычки программирования (хорошие или плохие), которые могут сформироваться в результате ленивой оценки Хаскеллом? Мне нравится идея чистоты функционального программирования на Haskell в целях понимания функционального программирования. Я просто немного беспокоюсь о двух вещах:

  1. Я могу неверно истолковать функции, основанные на ленивой оценке, как часть "функциональной парадигмы".
  2. Я могу разработать шаблоны мышления, которые работают в ленивом мире, но не в нормальном мире порядка / стремления к оценке.

Ответы [ 7 ]

21 голосов
/ 23 сентября 2010

При программировании на ленивом языке есть привычки, которые не работают на строгом языке. Некоторые из них кажутся программистам на Haskell настолько естественными, что они не считают их ленивыми оценками. Несколько примеров из головы:

f x y = if x > y then .. a .. b .. else c
  where
    a = expensive
    b = expensive 
    c = expensive

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

Другой пример, который приходит на ум, это «нумерация вещей»:

pairs = zip xs [1..]

здесь мы просто хотим связать каждый элемент в списке с его индексом, а архивирование с бесконечным списком [1..] является естественным способом сделать это в Haskell. Как вы пишете это без бесконечного списка? Ну, складка не слишком читабельна

pairs = foldr (\x xs -> \n -> (x,n) : xs (n+1)) (const []) xs 1

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

Я уверен, что есть еще много. Лень на удивление полезен, когда к ней привыкаешь.

11 голосов
/ 23 сентября 2010

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

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

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

6 голосов
/ 23 сентября 2010
  1. Я могу неправильно истолковывать функции, основанные на ленивой оценке, как часть "функциональной парадигмы".

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

Вы видите, что люди явно реализуют / вызывают его (особенно в форме ленивых последовательностей) на языках, которые не делают его по умолчанию; и хотя смешивание его с императивным кодом требует осторожности, чистый функциональный код позволяет безопасно использовать лень. А поскольку лень делает многие конструкции чище и естественнее, это прекрасно подходит!

(Отказ от ответственности: нет опыта работы с Haskell или F #)

1 голос
/ 23 сентября 2010

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

Это означает, что ленивая оценка строго более выразительна, чем нетерпеливая оценка.Позволяя вам писать более правильные и полезные выражения, он расширяет ваш «словарный запас» и способность мыслить функционально.

Вот один пример того, почему: язык может быть ленивым по умолчанию, но с дополнительным рвением илипо умолчанию стремится к необязательной лени, но на самом деле было показано (например, Окасаки), что существуют определенные чисто функциональные структуры данных, которые могут достигать только определенных порядков производительности, если реализованы на языке, который обеспечивает лень либо опционально, либо по умолчанию.

Теперь, когда вы хотите беспокоиться об эффективности, тогда разница имеет значение, и иногда вам захочется быть строгим, а иногда нет.

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

Редактировать: Вдохновленный постом Саймона, еще один момент: многие проблемы наиболее естественно воспринимаются как обход бесконечных структур, а не в основном рекурсивный или итеративный.(Хотя такие обходы обычно включают в себя некоторый рекурсивный вызов.) Даже для конечных структур очень часто вы хотите исследовать только небольшую часть потенциально большого дерева.Вообще говоря, нестрогая оценка позволяет вам не путать оперативную проблему того, что процессор пытается решить, с семантической проблемой наиболее естественного способа представления фактической структуры, которую вы используете.

0 голосов
/ 24 сентября 2010

Недавно я обнаружил, что занимаюсь программированием в стиле Haskell на Python.Я взял монолитную функцию, которая извлекала / вычисляла / генерировала значения и помещала их в приемник файлов за один шаг.

Мне показалось, что это плохо для понимания, повторного использования и тестирования.Мой план состоял в том, чтобы разделить создание ценности и обработку стоимости.В Haskell я бы сгенерировал (ленивый) список этих вычисленных значений в чистой функции и выполнил бы постобработку в другой (имеющей побочные эффекты) функции.Python может быть дорогим, если они стремятся стать большими, я подумал о следующем близком решении Python.Для меня это было использовать генератор для шага генерации стоимости.

Код Python стал намного лучше благодаря моему ленивому (каламбурному) мышлению.

0 голосов
/ 23 сентября 2010

Я ожидаю вредных привычек.

Я видел, как один из моих коллег пытался использовать (вручную) ленивую оценку в нашем .NET-проекте.К сожалению, следствие ленивой оценки скрыло ошибку, когда он пытался выполнить удаленные вызовы до начала выполнения main, и, таким образом, за пределами try / catch для обработки случая «Эй, я не могу подключиться к Интернету».

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

0 голосов
/ 23 сентября 2010

Что ж, попробуйте придумать что-нибудь, что сработало бы, если бы лениво оценивалось, а не если бы жадно оценивалось.Наиболее распространенной категорией из них будет ленивая оценка логического оператора, используемая для сокрытия «побочного эффекта».Я буду использовать язык C # -ish для объяснения, но функциональные языки будут иметь аналогичные аналоги.

Возьмем простую лямбду C #:

(a,b) => a==0 || ++b < 20

В языке с ленивым вычислением, если a == 0, выражение ++ b <20 не оценивается (поскольку все выражение в любом случае оценивается как true), что означает, что b не увеличивается.Как в императивном, так и в функциональном языках это поведение (и аналогичное поведение оператора AND) можно использовать для «скрытия» логики, содержащей побочные эффекты, которые не должны выполняться: в данном случае </p>

(a,b) => a==0 && save(b)

«a»может быть количество ошибок проверки.Если были ошибки проверки, первая половина терпит неудачу, а вторая половина не оценивается.Если ошибок валидации не было, оценивается вторая половина (которая включает в себя побочный эффект попытки сохранить b), и результат (очевидно, истинный или ложный) возвращается для оценки.Если какая-либо из сторон оценивается как false, лямбда возвращает false, указывая, что b не был успешно сохранен.Если бы это оценивалось «с нетерпением», мы бы попытались сохранить независимо от значения «а», что, вероятно, было бы плохо, если бы ненулевое «а» указывало, что мы не должны.

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

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