main = do
input <- sequence [getLine, getLine, getLine]
mapM_ print input
Давайте посмотрим на эту программу в действии:
m@m-X555LJ:~$ runhaskell wtf.hs
asdf
jkl
powe
"asdf"
"jkl"
"powe"
Удивительно для меня, но здесь, похоже, нет лени.Вместо этого все 3 getLine
с оцениваются с нетерпением, считанные значения сохраняются в памяти, а затем, не раньше, все печатаются.
Сравните с этим:
main = do
input <- fmap lines getContents
mapM_ print input
Давайте посмотримэто в действии:
m@m-X555LJ:~$ runhaskell wtf.hs
asdf
"asdf"
lkj
"lkj"
power
"power"
Совершенно разные вещи.Строки читаются одна за другой и печатаются одна за другой.Что странно для меня, потому что я действительно не вижу различий между этими двумя программами.
Из LearnYouAHaskell:
При использовании с действиями ввода / вывода sequenceA
являетсятоже самое что и sequence
!Он принимает список действий ввода-вывода и возвращает действие ввода-вывода, которое будет выполнять каждое из этих действий и в результате будет иметь список результатов этих действий ввода-вывода.Это связано с тем, что для преобразования значения [IO a]
в значение IO [a]
для выполнения действия ввода-вывода, которое при выполнении выдает список результатов, все эти действия ввода-вывода должны быть упорядочены таким образом, чтобы они затем выполнялись однимпосле другого, когда оценка является принудительной.Вы не можете получить результат действия ввода-вывода, не выполнив его.
Я в замешательстве.Мне не нужно выполнять ВСЕ действия ввода-вывода, чтобы получить результаты только одного.
Несколькими параграфами ранее книга показывает определение sequence
:
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
Хорошая рекурсия;ничто здесь не намекает мне на то, что эта рекурсия не должна быть ленивой, как и в любой другой рекурсии, чтобы получить заголовок возвращаемого списка, Haskell не должен проходить через ВСЕ шаги рекурсии!
Сравните:
rec :: Int -> [Int]
rec n = n:(rec (n+1))
main = print (head (rec 5))
В действии:
m@m-X555LJ:~$ runhaskell wtf.hs
5
m@m-X555LJ:~$
Очевидно, что рекурсия здесь выполняется лениво, а не жадно.
Тогда почему рекурсия в примере sequence [getLine, getLine, getLine]
выполняется
Что касается , почему важно, чтобы действия ввода-вывода выполнялись по порядку независимо от результатов: представьте себе действие createFile :: IO ()
и writeToFile :: IO ()
.Когда я делаю sequence [createFile,
writeToFile]
, я надеюсь, что они и выполнены и в порядке, хотя мне все равно, какие у них реальные результаты (оба очень скучные значения ()
)вообще!
Я не уверен, как это относится к этому вопросу.
Может быть, я скажу свой вопрос таким образом ...
В моемимейте в виду:
do
input <- sequence [getLine, getLine, getLine]
mapM_ print input
должно уменьшать до чего-то вроде этого:
do
input <- do
input <- concat ( map (fmap (:[])) [getLine, getLine, getLine] )
return input
mapM_ print input
Что, в свою очередь, должно уменьшать до чего-то вроде этого (псевдокод, извините):
do
[ perform print on the result of getLine,
perform print on the result of getLine,
perform print on the result of getLine
] and discard the results of those prints since print was applied with mapM_ which discards the results unlike mapM