- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
fibs = 0:1:zipWith (+) fibs (tail fibs)
fib = (fibs !!)
main = let
a = [fib 250000..]
b = a!!0
c = 1
in b `seq` print c
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
−95
fibs = 0:1:zipWith (+) fibs (tail fibs)
fib = (fibs !!)
main = let
a = [fib 250000..]
b = a!!0
c = 1
in b `seq` print c
Haskell не может в not used expression elimination. Не используемые константы a и b не убрал из вычисления.
В результате видим пустую трату времени time: 13.15s :
http://ideone.com/41Q8D
И это то ленивом языке, не смотря на то, что эти вычисления не нужны. Можно писать в багтреккер.
P.S.: Когда уже хаскель в подсветку говнокода добавят?
HaskellGovno 26.07.2012 19:01 # −4
http://ideone.com/nkoXr
>time: 0.01s
C# LINQ Enumerable.Count станет вычислять каждый элемент списка, так что там подсчет кол-ва на долго затянется.
vercetti 26.07.2012 19:15 # +1
bormand 26.07.2012 19:20 # +3
P.S. HaskellGovno сам заставил хаскель не лениться и вычислить b, и сам обосрал его за то, что он его вычисляет...
HaskellGovno 26.07.2012 19:21 # −4
http://ideone.com/pV04A
Ну а так баг пропадает:
http://ideone.com/h53JK
bormand 26.07.2012 19:23 # +2
Правильно. Потому что WHNF - раскрытие до первого попавшегося конструктора. В данном случае этим конструктором будет конструктор списка. Ни голова списка, ни его хвост вычисляться не будут.
HaskellGovno 26.07.2012 19:26 # −4
HaskellGovno 26.07.2012 19:35 # −4
Хаскель к ним, как мы видим, не относится. Никакого выкидывания не используемых переменных во время компиляции. Глупо. Вся надежда только на лень. То есть не используемые выражения выкидываются только в рантайме. Глупо. В результате лишние тормоза и пустое разрастание экзешника.
bormand 26.07.2012 19:39 # +1
Толсто же...
seq это специальный оператор, семантика которого заставляет хаскель вычислить левый аргумент до первого конструктора. Зачем было писать seq, если этот эффект не требуется?
HaskellGovno 26.07.2012 19:48 # −5
Люди тоже ошибаются, а Хаскель проектировался, как думающий за программиста и всё проверяющий язык.
Между прочем, хаскель, если наконец прочитаете документацию, имеет право опустить рекомендацию seq, если она будет не оптимальна. Именно поэтому появилось требование pseq. Только pseq гарантированно оптимизатор Хаскеля опустить не имеет права. seq лишь рекомендация, в отличии от требования pseq.
bormand 26.07.2012 19:58 # +1
seq :: a -> b -> bSource
Evaluates its first argument to head normal form, and then returns its second argument as the result.
http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:seq
http://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1260006.2
Пруф про то, что seq рекомендация, в студию.
HaskellGovno 26.07.2012 20:11 # −2
>The difference is subtle. The semantics of seq and pseq are identical; however, GHC can see that seq is strict in both its arguments and hence could choose to evaluate them in either order, whereas pseq is only strict in its first argument as far as the strictness analyser is concerned. The point is that pseq is useful for controlling evaluation order, which is what you want for adding parallelism to a program. seq, on the other hand, is not useful for controlling evaluation order. The documentation in 6.6 is also incorrect on this point.
bormand 26.07.2012 20:22 # +2
seq строго по левому аргументу, и не специфицировано по правому. Это не противоречит документации, в которой про правую часть умалчивается, а говорится только о том, что левая часть вычислится до первого конструктора, перед тем, как seq вернет результат.
pseq строго только в первом аргументе и возвращает правую часть невычисленной.
Но как это связано с тем, что вы написали?
> Между прочем, хаскель, если наконец прочитаете документацию, имеет право опустить рекомендацию seq, если она будет не оптимальна. Именно поэтому появилось требование pseq. Только pseq гарантированно оптимизатор Хаскеля опустить не имеет права. seq лишь рекомендация, в отличии от требования pseq.
bormand 26.07.2012 19:35 # +3
Результат snd res дальше не используется. Но если во втором элементе тупла будет undefined или какое-то длинное вычисление, то оно вылетит\выполнится именно при этом вызове, а не когда кто-то через пару часов полезет смотреть res.
А ваш код аналогичен случаю, если уебать молотком по пальцам, и говорить, а чего это он бьется, это явно бага...
HaskellGovno 26.07.2012 19:59 # −4
let (a,b) = f
В результате вычисляется неиспользуемое ниже выражение справа от =. Вот тебе и ленивый язык... Зато напридумывали костылей ~, исправляющих это:
let ~(a,b) = f
vistefan 26.07.2012 20:02 # +2
bormand 26.07.2012 20:03 # +2
let не вычисляет выражений. И = не вычисляет. До тех пор, пока выражение описанное в let не будет задействовано в каком-то вычислении, хаскель даже не почешется его вычислять.
P.S. let (a,b) = undefined in 2
unu-foja 26.07.2012 21:08 # +1
HaskellGovno 26.07.2012 19:24 # −6
unu-foja 26.07.2012 19:33 # +3
> Не используемые константы a и b не убрал из вычисления.
bormand уже написал, но ты реально толст. seq - и есть устранение ленивости, просто использовать нужно по необходимости.
HaskellGovno 26.07.2012 19:41 # −4
Нет. Имеет смысл вычислять левую часть seq только в случае, если она используется в правой части. А тут получается реально левая часть seq никак не используется и вычислять её не имеет смысла, а она все ровно впустую вычисляется, тк чистые функции все равно не имеют постэффектов.
А для устранения ленивости монады.
bormand 26.07.2012 19:48 # +1
Выше я привел пример (хоть и синтетический), когда результат левой части seq не содержится в правой, но тем не менее отбрасывать ее не стоит.
>она все ровно впустую вычисляется
Не впустую, несмотря на то, что она не используется в правой части явным образом, она могла бы использоваться там неявно, например входить в правую часть, как компонент некого списка\тупла\ADT. А компилятор не будет трекать все эти зависимости, только ради того, чтобы защититься от дурака, который написал seq, которое ему не требовалось...
unu-foja 26.07.2012 20:59 # +1
Для умолчательно ленивого языка нужно надежное средство форсировать вычисления. Считайте seq не частью языка, а мостиком в императивный мир (наряду с IO).
> A для устранения ленивости монады.
Будьте любезны пример вычисления sum от списка чисел.
HaskellGovno 26.07.2012 21:18 # −3
main = print $ sum [1..25000000]
unu-foja 26.07.2012 21:21 # +2
HaskellGovno 26.07.2012 21:31 # −4
bormand 26.07.2012 21:34 # +2
Ну сами же сказали "А для устранения ленивости монады".
Вот и реализуйте суммирование (не используя seq) так, чтобы оно не крашилось со stack/heap overflow из-за излишней ленивости...
HaskellGovno 26.07.2012 21:42 # −4
Не нужны тут никакие монады. Не придумывай. Суммирование реализовал, не крошится:
http://ideone.com/8mcun
sum' = foldl (+) 0
main = print $ sum' [1..25000000]
unu-foja 26.07.2012 21:45 # +1
А ленивость устранять нужно?
> , не крошится:
А теперь отключи оптимизации и удивись.
HaskellGovno 26.07.2012 21:47 # −4
Прошу, на сцену.
>А ленивость устранять нужно?
Нет.
unu-foja 26.07.2012 21:50 # +1
Кэп намекает, что ghc может оптимизировать этот случай, но не обязан (без -O и не будет). Другие компиляторы скорее всего отвалятся.
> Нет.
То есть по-вашему, это нормальный код? Вопросы отпали.
HaskellGovno 26.07.2012 21:56 # −5
http://ideone.com/8ZB1r
>Stack space overflow
HaskellGovno 26.07.2012 21:57 # −5
Нет, это не нормальный код, нормальный код я написал выше:
http://ideone.com/lpR0q
main = print $ sum [1..25000000]
>result: success time: 3.55s memory: 3588 kB returned value: 0
>output:
>312500012500000
bormand 26.07.2012 21:54 # +3
Фома вы наш неверующий...
HaskellGovno 26.07.2012 21:59 # −4
А на идеоне доказать? Ключи в тексте можно прямо указывать в {} А так я выдуманное тоже напечатать могу.
bormand 26.07.2012 22:03 # +1
Вот уж точно неверующий Фома..
http://ideone.com/Vtzln
HaskellGovno 26.07.2012 22:06 # −3
http://ideone.com/iXfTD
{-# OPTIONS_GHC -O0 #-}
import Data.List
sum' = foldl' (+) 0
main = print $ sum' [1..25000000]
bormand 26.07.2012 22:09 # +1
Сам учись:
http://hackage.haskell.org/packages/archive/base/latest/doc/html/src/Data-List.html#foldl%27
Там используют seq.
HaskellGovno 26.07.2012 21:51 # −5
Я не сказал, что seq не нужен. Я сказал, что не нужна бага, вычисляющая впустую выражение, помеченное в левой части seq, но реально ни где результат этого вычисления не используется.
HaskellGovno 26.07.2012 22:03 # −5
bormand 26.07.2012 22:06 # +1
P.S. Я поставил минус вторым. Ждем первого минуснувшего.
HaskellGovno 26.07.2012 22:22 # −4
HaskellGovno 27.07.2012 06:54 # −4
HaskellGovno 27.07.2012 07:15 # −2
http://ideone.com/0daW7
> result: success
> output: 1
http://ideone.com/oy8hf
>result: runtime error
>stderr: prog: Prelude.undefined
Работает...
bormand 27.07.2012 07:22 # +3
Нет, проспал на работу и только-только зашел на ГК.
Возьмем, к примеру, вот такой код:
http://ideone.com/p7AnA
Как видим, исключение про undefined выбросилось только тогда, как мы начали пользоваться результатом.
Немного переделаем его:
http://ideone.com/fDoUy
Теперь исключение возникло вовремя. И, как видим, результат forceList (snd tuple) не используется в правой части.
HaskellGovno 27.07.2012 11:29 # −2
http://ideone.com/tBIzJ
forceList [] = ()
forceList (x:xs) = x `seq` forceList xs
bormand 27.07.2012 11:42 # +1
Ну не хвосты, а пустой список, но да (), в данном случае лучше.
HaskellGovno 27.07.2012 11:43 # −2
HaskellGovno 27.07.2012 11:38 # −4
Тогда бы можно было форсировать любое выражение, но конечно же глупый хацкель не может в перегрузку функций.
roman-kashitsyn 27.07.2012 11:42 # +2
bormand 27.07.2012 11:46 # +2
Так можно же ;) Но правда не совсем любое, а то, что принадлежит классу NFData: http://hackage.haskell.org/packages/archive/deepseq/1.3.0.0/doc/html/Control-DeepSeq.html
Инстансы для примитивных типов, списков и туплов (т.е. все что вами перечислено выше) там уже описаны, а вот для пользовательских ADT нужно описать инстанс самому, что не совсем удобно, но терпимо.
HaskellGovno 27.07.2012 11:53 # −4
Понятно, что делайте не более чем для туплов 3 элементов, а то много писать придется.
HaskellGovno 27.07.2012 12:03 # −4
Не удалось... prog.hs:1:7:
Could not find module `Control.DeepSeq':
Use -v to see a list of the files searched for.
bormand 27.07.2012 12:17 # +1
force там уже готов, но в старой версии, которая у меня стоит его почему-то нет. Поэтому в примере добавлена реализация force через deepseq, такая же как в новой либе.
Вот так вот можно описать инстанс NFData для своих типов:
Инстансы для примитивов, списков и туплов можно посмотреть тут:
http://hackage.haskell.org/packages/archive/deepseq/1.3.0.0/doc/html/src/Control-DeepSeq.html#rnf (для примитивов используется дефолтовое rnf a = a `seq` ()).
HaskellGovno 27.07.2012 11:54 # −4
>force (x) = force x `seq` ()
Туплов одного элемента не бывает.
zim 26.07.2012 21:57 # +7
Откуда вы такие лезете.
TarasB 27.07.2012 12:28 # +4
Никогда. Потому что хаскелл - это функциональный небыдло-язык для элиты, код на нём не может быть говнокодом. А если тебе кажется, что это говнокод - значит ты, быдло, просто не смог осилить своим жалким умишком каррированное замыкание игрек-комбинатора.
guest 27.07.2012 17:21 # +1
HaskellGovno 27.07.2012 16:40 # −4