2015-04-25 4 views
0

У меня есть 2 списка, которые я пытаюсь заполнить. При чтении из stdin, в зависимости от значения одной из прочитанных вещей, я хочу добавить в другой список. Пример,Haskell добавить к списку условно

import Control.Monad(replicateM) 

main = do 
    n <- getLine 
    let l1 = [], l2 = [] 
     in replicateM (read n) (getLine >>= (\line -> 
      case line of "Yes" -> 
       -- do something with line 
       -- and append value of that thing to l1 
          "No" -> 
       -- do something else 
       -- append this value to l2 
      putStrLn line)) 

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

Это ответ, который я придумал

В то время как мы в этом, может кто-нибудь объяснить, почему это дает мне бесконечный список:

let g = [] 
let g = 1:g 
-- g now contains an infinite list of 1's 

Это то, что я наконец придумал с:

import Control.Monad(replicateM) 
import Data.Either 

getEither::[String] -> [Either Double Double] 
getEither [] = [] 
getEither (line:rest) = let [n, h] = words line 
          fn = read f :: Double 
          e = case heist of "Yes" -> Left fn 
               "No" -> Right fn 
          in e : getEither rest 

main = do 
    n <- getLine 
    lines <- replicateM (read n) getLine 
    let tup = partitionEithers $ getEither lines :: ([Double], [Double]) 
    print tup 

Не знаете, как fmap могли быть использованы в данном случае

ответ

3

Вот короткая сессия GHCi, которые могут дать вам некоторые идеи:

> :m + Control.Monad Data.Either 
> partitionEithers <$> replicateM 3 readLn :: IO ([Int], [Bool]) 
Left 5 
Right True 
Left 7 
([5,7],[True]) 

Ответ на ваш второй вопрос, что пусть рекурсивно; поэтому два g s в let g = 1:g относятся к одному и тому же объекту в памяти.

+0

Что это '<$>' символ? – smac89

+0

@ Smac89 Это символ оператора для 'fmap' – Sibi

+1

@ Smac89' (<$>) 'может быть записан многими другими способами, один из которых может быть вам знаком:' fmap', 'liftA',' liftM'. Вы можете также думать об этом как '\ f ma -> do {a <- ma; return (f a)} ', хотя он немного более общий. –

3

Вы думаете в терминах изменяемых переменных: вы «инициализируете» l1,l2 в пустой список, а затем рассуждаете об обновлении их длинными списками. Этот дизайн отлично работает в императивном программировании, но не так просто в чисто функциональном программировании, поскольку он включает в себя мутацию.

Теперь, даже в чисто функциональном программировании у нас есть способы имитировать мутацию, через монады. Например, однажды можно достичь мутации здесь через IORefs или StateT IO. В этом случае, однако, это будет излишне сложный способ решения задачи.

Вы хотите добавить данные для формирования двух списков. Вы хотите использовать replicateM, что хорошо. Дело в том, что replicateM построит только один список, а не два. Вопрос в том, как мы можем создать список, который легко разбивается на два?

Первая некрасиво попытка создать список помеченных значений, то есть список пар:

case line of 
    "Yes" -> let value = ... in 
      return ("for l1", value) 
    "No" -> let value = ... in 
      return ("for l2", value) 

Делая это сделало бы replicateM составить список таких как

[("for l1", value1), ("for l1", value2), ("for l2", value3), ...] 

, который мы можем затем разделить на два списка.

Использование строк для тегов выглядит однако немного unelegant, так как булева будет достаточно:

case line of 
    "Yes" -> let value = ... in 
      return (True, value) 
    "No" -> let value = ... in 
      return (False, value) 

Еще лучше было бы использовать тип Either a b:

case line of 
    "Yes" -> let value1 = ... in 
      return (Left value1) 
    "No" -> let value2 = ... in 
      return (Right value2) 

Приятной следствием вышесказанного является то, что value1 и value2 могут быть даже разных типов. Предыдущие фрагменты заставляли их делиться своим типом: поскольку мы создаем список пар, каждая пара должна иметь один и тот же тип. Новый список теперь вместо типа [Either a b], где a - это тип значений, которые нужно поместить в l1, и b, что для l2.

Как только вы получите [Either a b], вы хотите разбить его на [a] и [b]. Как предлагает @DanielWagner в своем ответе, вы можете использовать для этого partitionEithers.

Смежные вопросы