Как напечатать многострочную строку с помощью `putStrLn`?

Я пишу очень простую программу на Haskell, которая запрашивает у пользователя имя, а затем отображает некоторый текст, основанный на этом имени. Я могу заставить программу работать, но я хотел бы отобразить ASCII-изображение кота, когда пользователь вводит имя моей кошки. Я хотел бы что-то такое, как

  ^  -  ^
 ( .   . )
   =>;<=
  /     \
 |       |

Мой подход "давайте просто посмотрим, сработает ли это" заключался в использовании putStrLn. Это не работает Что я могу сделать? Благодарю. Моя попытка

if name == "cat"
    then putStrLn ("      ^  -  ^
                         ( .   . )
                           =>;<=
                          /     \
                         |       |")

2 ответа

Решение

Ли спросил: "Как не putStrLn работа?", и вы ответили:

if name == "cat"
    then putStrLn ("      ^  -  ^
                         ( .   . )
                           =>;<=
                          /     \
                         |       |")

Вы должны включить сообщения об ошибках. Поместив это в функцию верхнего уровня (test name = ...) код не компилируется с:

so.hs:3:38: error:
    lexical error in string/character literal at character '\n'

Это потому, что вы не можете иметь переводы строк в строковых литералах Haskell. Многострочные строки в Haskell требуют специального экранирования (обратная косая черта в начале и в конце каждой строки. У вас уже есть неэкранированная обратная косая черта в ascii art, которая должна была быть экранирована, поэтому мы исправим это тоже:

 test name =
        if name == "cat"
    then putStrLn ("      ^  -  ^\n\
                   \     ( .   . )\n\
                   \       =>;<=\n\
                   \      /     \\\n\
                   \     |       |")

Теперь мы получаем еще одну ошибку, потому что в Haskell if операторы больше похожи на троичный оператор и поэтому требуют, чтобы ветвь else оставалась хорошо типизированной. Полученный код:

test name =
        if name == "cat"
        then putStrLn ("      ^  -  ^\n\
                       \     ( .   . )\n\
                       \       =>;<=\n\
                       \      /     \\\n\
                       \     |       |")
        else return ()

В качестве альтернативы вы можете использовать квази-кавычки для более симпатичных многострочных строк. Это требует string-qq пакет и расширение языка квазиквот. Расширение имеет синтаксис [|<quoter name>|<string>|] а пакет string-qq содержит квотер с именем s для строк:

{-# LANGUAGE QuasiQuotes #-}
import Data.String.QQ
import Control.Monad (when)

test2 name =
    when (name == "cat") $ putStrLn [s|
      ^  -  ^
     ( .   . )
       =>;<=
      /     \\
     |       ||]

Вот еще одна альтернатива, которую я иногда использую:

putStr $ unlines 
  [ "      ^  -  ^"
  , "     ( .   . )"
  , "       =>;<="
  , "      /     \\"
  , "     |       |"
  ]

unlines это стандартная функция, которая берет список строк и объединяет их, а также вставляет символы новой строки после каждой строки (включая последнюю - поэтому я и использовал putStr вместо).

> unlines ["abc", "def", "ghi"]
"abc\ndef\nghi\n"
Другие вопросы по тегам