Haskell — проблема с симпатичной печатью список

Я плохо знаком с 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 ()]'. Я попробовал полдюжины вещей, и ни один из него, казалось, не помог. Что надлежащий путь состоит в том, чтобы сделать то, что я пытаюсь сделать?

11
задан RCIX 6 January 2010 в 21:44
поделиться

1 ответ

Немного пробела может помочь прояснить это для вас.

При записи 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


Из вики-страницы jqGrid documentation for Options :

hidegrid

boolean

Включение или отключение кнопки отображения/скрытия сетки, которая отображается в правой части слоя подписи. Вступает в силу, только если свойство caption не является пустой последовательностью.

-121--1302514-
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
  • Если в списке ничего не осталось, верните (введите в монаду) пустой список результатов.
  • В противном случае, в пределах монады,
    • Bind (для 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] или выше:)

26
ответ дан 3 December 2019 в 03:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: