Я могу иметь (келл) cheezburger?

Редактировать 13 июня:

Я сделал новую и улучшенную версию здесь .

Оригинальный вопрос

Это очень рудиментарный переводчик lolcats (он работает только на фразе «Могу ли я иметь чизбургер») в Haskell. Это моя первая попытка Haskell (или любой функциональный язык программирования). Я уверен, что мой код абсолютно зверский (документация была мрачной), но она работает.

Teh Codez

import Data.Strings
main = do
    putStrLn "Enter your text to translate"
    inputText <- getLine
    let capsText = strToUpper inputText
    let a = strReplace "HAVE" "HAS" capsText
    let b = strReplace "CAN I" "I CAN" a
    let c = strReplace "CHEESEBURGER" "CHEEZBURGER" b
    let d = strReplace " A " " " c
    putStrLn (d)
    getLine
27 голосов | спросил Michael Brandon Morris 8 J0000006Europe/Moscow 2016, 19:21:00

4 ответа


37

Хорошая практика в Haskell - отделить функциональный код от IO. В этом случае вы можете (и, следовательно, должны) определить lolcat :: String -> String. Обязательно поместите объявление типа для всех функций - вы не записали его для вашего main.

Определение переменных a, b, c и d является излишним. Я бы написал это как состав функций.

lolcat :: String -> String
lolcat = strReplace " A " " " .
         strReplace "CHEESEBURGER" "CHEEZBURGER" .
         strReplace "CAN I" "I CAN" .
         strReplace "HAVE" "HAS" .
         strToUpper
ответил 200_success 8 J0000006Europe/Moscow 2016, 19:33:53
22

Я согласен с 200_success и просто печатаю полную программу, потому что вы сказали, что являетесь новичком.

import Data.Strings

encode :: String -> String
encode = strReplace " A " " "
       . strReplace "CHEESEBURGER" "CHEEZBURGER"
       . strReplace "CAN I" "I CAN"
       . strReplace "HAVE" "HAS"
       . strToUpper

main :: IO ()
main = do 
   putStrLn "Enter your text to translate"

   input <- getLine
   putStrLn $ encode input

Основная функция, возможно, более часто записывается как:

main :: IO ()
main = do
   putStrLn "Enter your text to translate"
   putStrLn =<< encode <$> getLine

EDIT:

@MichaelKlein попросил меня объяснить (<$>) и (=<<), поэтому я сделаю это как можно короче.

<$> является встроенной версией fmap, которая является единственной функцией из класса Functor. Класс типа похож на интерфейс на императивном языке, таком как Java.

class Functor f where
   fmap :: (a -> b) -> f a -> f b

fmap используется для запуска функции (a -> b) внутри некоторой структуры данных f a

fmap (+1) [1]      == [2]
fmap (+1) (Just 3) == Just 4
(+1) <$> (Just 3)  == Just 4

(<$>) :: f a -> ( a -> b ) -> f b

В Haskell мы рассматриваем мир как datastructure IO. Таким образом, точно так же, как вы можете запускать функцию внутри списка или Maybe (например, Just 3), вы можете запустить функцию внутри IO.

encode             :: String -> String
getLine            :: IO String
encode <$> getLine :: IO String

(=<<) является частью класса Monad, , который трудно объяснить , потому что сначала вам нужно знать о нескольких других типах.

(=<<) :: (a -> m b) -> m a -> m b

(=<<) произносится как "bind" и является переворачиваемой версией более распространенного (>>=). Важным является то, что у Haskell есть специальный синтаксис для работы с монадами: нотация do.

Это:

main = do
  input <- getLine
  putStrLn input

Для этого используется синтаксический сахар:

main = getLine >>= \input -> putStrLn input

Что такое:

main = getLine >>= putStrLn


Все они также равны:

main = do
   putStrLn "burger"
   input <- getLine
   putStrLn $ encode input

main = putStrLn "burger" 
    >> getLine >>= \input -> putStrLn $ encode input

main = putStrLn "burger" 
    >> encode <$> getLine >>= putStrLn

main = do 
  putStrLn "burger" 
  putStrLn =<< encode <$> getLine 
ответил BlackCap 8 J0000006Europe/Moscow 2016, 22:01:25
9

Удалить повторение

Вы повторяете strReplace много:

        vvvvvvvvv
let a = strReplace "HAVE" "HAS" capsText
let b = strReplace "CAN I" "I CAN" a
let c = strReplace "CHEESEBURGER" "CHEEZBURGER" b
let d = strReplace " A " " " c
        ^^^^^^^^^^

Какие изменения следует заменить тем, что: [("HAVE", "HAS"), ("CAN I", "I CAN"), ...]

Поэтому ваша программа должна указать идею замены один раз и затем содержать этот список «инструкций замены».

Чтобы быть более понятным, функция, которую вы должны использовать, имеет тип String -> [(String, String)] -> String и пример использования:

> replaceByTuples "foobar" [("foo", "fuu"),("bar", "baz")]
"fuubaz"

С помощью этой функции с вашим конкретным списком замен будет легко.

Эта функция находится в этом блоке спойлера. Я советую вам попытаться реализовать его самостоятельно.

  

import Data.List.Utils;


replaceByTuples s t = (foldl (.) id $ map (\(start, end) -> replace start end) t) s
 Более подробную информацию и более подробные сведения об этой функции можно найти по адресу: https: //stackoverflow.com/questions/7862221/how-do-i-do-string-replace-in-haskell

ответил Caridorc 9 J0000006Europe/Moscow 2016, 15:09:42
5

Я согласен с двумя другими ответами, но я бы использовал interact в основном. Это сделает вашу программу бесконечно замкнутой, пока не будет больше входных данных, которые могут быть не такими, какие вы хотите, но это решит проблему сохранения консоли. Вам нужно будет определить функцию eachLine, чтобы программа обрабатывала одну строку за раз, как описано в этом ответе stackoverflow .

Как упоминалось в 200_success, рекомендуется писать сигнатуры типа для всех функций верхнего уровня, а main традиционно имеет подпись типа: main :: IO () , Из-за getLine в конце вашего основного текста у него будет подпись типа: main :: IO String, что даст вашей программе непреднамеренный побочный эффект печати дополнительная строка при завершении. Написание типов-подписи явно позволяет компилятору поймать такие проблемы.

Это будет полная программа:

import Data.Strings

main :: IO ()
main = do
    putStrLn "Enter your text to translate"
    interact (eachLine lolcat)

lolcat :: String -> String
lolcat = strReplace " A " " " .
         strReplace "CHEESEBURGER" "CHEEZBURGER" .
         strReplace "CAN I" "I CAN" .
         strReplace "HAVE" "HAS" .
         strToUpper

eachLine :: (String -> String) -> String -> String
eachLine f = unlines . map f . lines
ответил Matthew 9 J0000006Europe/Moscow 2016, 15:31:51

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132