Я плохо знаком с haskell, и я прочитал и переварил, Изучают Вас Haskell Для Большой Пользы, испытывая несколько вещей по пути. Для моего первого проекта я хотел судить классика: FizzBuzz. Таким образом, я придумал следующий код:
import System.IO
fizzBuzz :: (Integral a) => a -> String
fizzBuzz num
| fizz && buzz = "FizzBuzz"
| fizz = "Fizz"
| buzz = "Buzz"
| otherwise = show num
where fizz = num `mod` 3 == 0
buzz = num `mod` 5 == 0
main = print $ map fizzBuzz [1..100]
Работавший отлично, кроме я получил довольно плотно выглядящий список, который было трудно прочитать. Таким образом, я попробовал эту основную функцию вместо этого:
main = map putStrLn $ map fizzBuzz [1..100]
И это дает мне ошибку Couldn't match expected type 'IO t' against inferred type '[IO ()]'
. Я попробовал полдюжины вещей, и ни один из него, казалось, не помог. Что надлежащий путь состоит в том, чтобы сделать то, что я пытаюсь сделать?
Немного пробела может помочь прояснить это для вас.
При записи i = + 1
фактически происходит i = + 1
. Это связано с отсутствием оператора = + в C. =
рассматривается как собственный оператор, а +
- унарный оператор, действующий на константу 1
. Таким образом, оценка этого оператора начинается с правой стороны оператора =
. + 1
вычисляется как 1
, и оператор =
присваивает это значение переменной i
.
+ =
является собственным оператором в C, что означает "добавить значение выражения в правой части этого оператора к переменной в левой части и присвоить его этой переменной, так что что-то вроде следующего:
i = 3;
i += 2;
вычислит до 5
, потому что оператор + =
вычислит правую сторону оператора (в данном случае 2
, и добавит его в левую сторону (в данном случае i имеет значение 3) и назначит переменной слева. По существу, это становится i = (2 + 3)
. Поэтому переменная i
будет иметь конечное значение 5
.
При добавлении значения 1
к целочисленной переменной можно использовать оператор + +
. Добавление этого оператора после переменной (т.е. i++
) вызовет выполнение текущей строки кода до приращения переменной на единицу. Префиксирование оператора перед переменной приведет к выполнению инструкции после приращения переменной.
например:
i = 5;
j = i++;
приведет к i = 6
и 'j = 5', тогда как
i = 5;
j = ++i;
приведет к i = 6
и j = 6
.
Аналогично, оператор --
может использоваться для уменьшения (уменьшения) переменной на единицу, аналогично тому, как + +
будет увеличивать (увеличивать) переменную на единицу. К обоим операторам применяются одни и те же правила позиционирования оператора до или после переменной.
Надеюсь, это немного прояснит ситуацию.
-121--3210707- Да, для этого существует вариант: hidegrid: false
-121--1302514-hidegrid
boolean
Включение или отключение кнопки отображения/скрытия сетки, которая отображается в правой части слоя подписи. Вступает в силу, только если свойство caption не является пустой последовательностью.
map :: (a -> b) -> [a] -> [b]
putStrLn :: Show a => a -> IO ()
map putStrLn :: Show a => [a] -> [IO ()]
Вы получили список действий IO ()
.
main :: IO ()
Необходимо объединить их в одно действие ввода-вывода ()
.
Необходимо выполнить все действия IO ()
в последовательности / последовательности _ :
sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()
Для удобства: mapM / mapM _ сопоставит функцию со списком и упорядочит полученные монадические результаты.
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
Поэтому ваш фиксированный код будет выглядеть следующим образом:
main = mapM_ putStrLn $ map fizzBuzz [1..100]
Хотя я, вероятно, написал бы его так:
main = mapM_ (putStrLn . fizzBuzz) [1..100]
Или даже так:
main = putStr $ unlines $ map fizzBuzz [1..100]
Давайте напишем нашу собственную последовательность
. Что мы хотим сделать?
sequence [] = return []
sequence (m:ms) = do
x <- m
xs <- sequence ms
return $ x:xs
IO
monad это означает выполнение) первый результат. последовательность
остальная часть списка; связать этот список результатов. Библиотека GHC использует нечто большее, чем foldr (liftM2 (:)) (возврат [])
, но это труднее объяснить новичку; пока просто поверь мне, что они равноценны.
sequence _
проще, так как это не мешает отслеживать результаты. Библиотека GHC реализует его как последовательность _ ms = foldr (> >) (return ()) ms
. Давайте просто расширим определение foldr
:
sequence [a, b, c, d]
= foldr (>>) (return ()) [a, b, c, d]
= a >> (b >> (c >> (d >> return ())))
Другими словами, "do a
, отбросим результат; b
; отбросить результат,... наконец, возврат ()
".
mapM f xs = sequence $ map f xs
mapM_ f xs = sequence_ $ map f xs
С другой стороны, вообще не нужно знать монад с альтернативным решением unlines
.
Что делает unlines
? Ну, строки «a\nb\nc\nd\n» = [«a», «b», «c», «d»]
, так что, конечно, unlines [«a», «b», «c», «d»] = «a\nb\nd\n»
.
unlines $ map fizzBuzz [1.. 100]
= unlines [«1», «2», «Fizz»,..]
= «1\n2\nFizz\n»...
и от него переходит в putStr
. Благодаря магии лени Хаскелла, полный ряд никогда не нужно строить в памяти, поэтому это с радостью достанется [1.. 1000000]
или выше:)