Comments 35
Ближайшее место, где начинается веселье — база данных с конкурентным доступом. Все виды ORM, которые я видел, на Хаскеле выглядели так же трагично, как и на других языках. Начинают красиво, а дальше квирк на хаке и воркэраундом погоняет.
Знакомство с фяп научило меня видеть чистые функции там где раньше была борода из классов и сайд эффектов. Но работать все-таки приходится на ооп.
Не хочу сказать, что это выглядит как-то там особо прекрасно, но и трагичности в этом тоже не наблюдается. Да, это делается не так, как привычно, иногда даже совсем не так. Но привычка — это все же немного другое, согласитесь. Это даже называется FRM, а не ORM, потому что маппинг тут не в объекты, а с функциями. Но опять же — это как раз естественно для функционального языка.
где мне нужно было срезать еще 10 символов, чтобы получить самое короткое решение
Серьезно? Ну я догадываюсь, что вы имели в виду заменить concatMap на монадический бинт. Но там и остальное можно неплохо ужать:
months = cycle [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
dude m d n = sum . take n . drop (d-1) $ flip take [1,3..] =<< drop (m-1) months
main = print $ dude 12 31 10
Вместо flip take [1,3..]
можно же написать
(`take`[1,3..])
Будет еще короче
Эта статья не про самое короткое решение, а про решение вообще. В конце написано об этом. Ужать можно еще сильнее.
Только если уж вы пишете для начинающих, то советы типа «залезть в prelude и найти там нечто» — они не очень хорошие. Ибо откуда начинающий догадается, что ему нужно? Даже в случае суммы это далеко не так очевидно, как вы показываете. Не говоря уже про применение map, которое вы подаете так, как будто всем это давно известно. На самом деле, тем кому это очевидно, уже не нужно введение такого уровня — им захочется посложнее.
Ну и так, по мелочи — хорошо бы через проверку правописания пропустить, есть некоторое немаленькое число опечаток.
Так вот в статье как раз и написано где нужно смотреть и дана ссылка. Теперь начинающий знает, что есть такое место и что там можно что-то найти.
А что не известного в map? В питоне и яваскрипте он есть. Большинство людей с ним знакомы. Я не ставлю себе целью научить людей программировать. Статья для тех кто уже умеет, но боится хаскелля.
В продолжении будет посложнее, придется рассказать про типы и слово на букву м. Для одной статьи было бы слишком много.
Прошу прощения. Видимо, хромовый спеллчекер не очень. Сейчас поищу какой-нибудь сервис и проверю.
Спасибо за замечание. Я часто мучаюсь из за того, что не могу понять, что нужно рассказать. Что очевидно, а что требует пояснения.
И не только в этом месте — ленивость в общем виде например позволяет написать if в виде функции с тремя аргументами, при этом не вычисляя аргумент для else до тех пор, пока он не потребуется. А если у else есть побочные эффекты, то вычислять его всегда — это моветон. Ну вы поняли, я думаю.
Побочные эффекты в чистых функциях — моветон независимо от ленивости или неленивости
Речь о том, что ленивость это как раз то, что сильно отличает хаскель от большинства других языков, где есть скажем такие же на первый взгляд map, fold и списки, потому что большинство из этих других языков все-таки не ленивые.
И на самом деле, если смоделировать поведение неленивого языка в хаскеле можно, и достаточно несложно, то чтобы смоделировать ленивость в изначально неленивом языке, придется довольно сильно напрягаться (и не всегда выйдет). Скажем, в java это стало делать сравнительно легко, когда появились лямбды. А если нет функций как 1st class object, то и вообще вряд ли получится.
И именно ленивость дает возможность использовать бесконечные структуры данных.
Статья для тех кто уже умеет, но боится хаскелля.
Я боюсь не haskell, а cabal. От того вместо haskell играюсь с clojure.
Где же самое короткое решение, спросите вы? Там такое дело, в общем, есть слово на букву м.Хорошая статья, пишите про букву м. :)
А что тут, собственно, магического?
to_days max_days
— это заголовок функции, говорящий что у нее есть 1 аргумент и задающий имя этого аргумента.
[1..max_days]
это конструктор списка последовательных значений
Прямые аналоги:
function to_days(max_days) {
return _.range(1, max_days+1);
}
def to_days(max_days):
return xrange(1, max_days+1)
static IEnumerable<int> ToDays(int max_days) => Enumerable.Range(1, max_days);
Функция to_days
с аргументом max_days
которая при помощи синтаксического сахара [1..max_days]
создает список от 1
до max_days
. Ну т.е. [1..10] == [1, 2, 3, 4, 5, 6, 7, 8, 8, 10]
.
Gryphon88 сорри, не в ту ветку ответил
Ничего страшного. Спрашивайте еще; мне нужно знать, что я не понятно объясняю.
sum
является достижение конца списка. Если искать сумму бесконечного списка то конечное условие никогда не будет достигнуто и все поломается. Для функций же вроде take
или drop
конечным условием является перебор заданного количества элементов, поэтому им все-равно где заканчивается список, они "проходят" только n заданных шагов, а значит map
который перед ними выполняет только эти n
шагов.ну т.е. даже будь у вас конечный список, если для получения результата вы берете только первые три элемента, то только они и будут вычислены.
Что значит "в разной семантике"?
Есть еще замечательный оператор $ который как бы говорит хаскеллю: «сначала выполни все, что правее меня, а результат подставь на мое место».
По моему такое определение отражает суть оператора $ с искажением. В противном случае, в выражении
take 3 . reverse . filter even $ [1..10]
логично было бы убрать $, однако компилятор в таком случае выдаст ошибку:take 3 . reverse . filter even [1..10]
-- take 3 . reverse . [2,4,6,8,10]
-- ошибка: применение композиции (.) к списку
Получается что оператор $ в данном примере говорит хаскеллю: «сначала выполни все, что левее меня, а результат подставь на мое место». А версия со скобками выглядит так:
(take 3 . reverse . filter even) [1..10]
Даже в одной из книг, помню, было определение типа «сначала выполни все что справа, потом примени». Со временем появились сомнение, и выяснилось, что '$' такой же оператор применения как ' ' (пробел), только первый имеет самый низкий приоритет — 0, а второй — самый высокий — 10.
К слову, оператор композиции (.) имеет приоритет 9, что объясняет ошибку компилятора в примере выше.
Спасибо за статью, с удовольствием прочитаю продолжение!
Говорить "сначала выполни все, что левее меня" — тоже ошибка, потому что приоритет "работает" в обоих направлениях: foo $ bar baz
будет вычислено как foo (bar baz)
А в книгах пишут "сначала правее" подразумевая, что оператор $ — правоассоциативный. Иными словами, foo $ bar $ baz
эквивалентно foo (bar baz)
, а вовсе не (foo bar) baz
. В этом заключается его еще одно отличие от левоассоциативного пробела.
Говорить «сначала выполни все, что левее меня» — тоже ошибка, потому что приоритет «работает» в обоих направлениях:Совершенно верно. Это был лишь пример другого «искажения» противоречащего с первым.
в книгах пишут «сначала правее» подразумевая, что оператор $ — правоассоциативный.Отличие между оператором $ и обычным применением, конечно, не только в приоритете, но и в ассоциативности (в предыдущем комментарии я поторопился и пропустил это). Однако ассоциативность оператора с самым низким приоритетом может быть задействована, после того, как всё остальное в выражении приведено в нормальную форму. В этом и заключается «искажение»: вербальное подчеркивание именно ассоциативности по неволе затмевает важность приоритета вычисления. А это, в свою очередь, приводит к недопониманию в процессе изучения, о чем я собственно и написал.
Про хаскелль для самых маленьких на примере задачи с codefights