2016-12-01 3 views
0

Я написал код Haskell, как:Haskell: Печать номер дела

loop = do 
x <- getLine 
if x == "0" 
    then return() 
    else do arr <- replicateM (read x :: Int) getLine 
      let blocks = map (read :: String -> Int) $ words $ unwords arr 
      putStr "Case X : output = "; -- <- What should X be? 
      print $ solve $ blockPair blocks; 
      loop 

main = loop 

Это заканчивается на 0 входе. Я также хочу напечатать номер дела, например. Case 1, 2 ...

Пример запуска:

1 
10 20 30 
Case 1: Output = ... 
1 
6 8 10 
Case 2: Output = ... 
0 

Кто-нибудь знает, как это можно сделать? Кроме того, если возможно, вы можете предложить мне способ распечатать выходную строку в самом конце?

Заранее спасибо.

+1

«Также, если возможно, вы можете предложить мне способ печати выходной линии в самом конце?» - Я не уверен, что еще вы хотели бы напечатать, и в какой момент это должно произойти. – duplode

+0

@duplode, он должен работать как - все входы в начале, а затем после того, как пользователь вводит 0, он должен распечатать выходы. – user7201762

+0

Мне нужна помощь, пожалуйста, – user7201762

ответ

4

Для первой части вашего вопроса текущий номер случая является примером некоторого «состояния», которое вы хотите сохранить в ходе выполнения вашей программы. На других языках вы, несомненно, будете использовать изменяемую переменную.

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

main = loop 1 

loop n = do 
    ... 
    putStr ("Case " ++ show n ++ ": Output = ...") 
    ... 
    loop (n+1) -- update "state" for next loop 

во втором часть вашего вопроса немного более сложна. Похоже, вы хотели намек вместо решения. Чтобы вы начали, позвольте мне показать вам пример функции, которая читает строки до тех пор, пока пользователь не войдет в end, а затем вернет список всех строк до, но не включая end (вместе с функцией main, которая делает что-то интересное с линиями используя в основном чистый код):

readToEnd :: IO [String] 
readToEnd = do 
    line <- getLine 
    if line == "end" 
     then return [] 
    else do 
     rest <- readToEnd 
     return (line:rest) 

main = do 
    lines <- readToEnd 
    -- now "pure" code makes complex manipulations easy: 
    putStr $ unlines $ 
     zipWith (\n line -> "Case " ++ show n ++ ": " ++ line) 
       [1..] lines 

Edit: Я думаю, что вы хотели больше прямого ответа, а не намек, так как вы бы адаптировать выше подход к чтению списка блоков было бы написать что-то вроде:

readBlocks :: IO [[Int]] 
readBlocks = do 
    n <- read <$> getLine 
    if n == 0 then return [] else do 
    arr <- replicateM n getLine 
    let block = map read $ words $ unwords arr 
    blocks <- readBlocks 
    return (block:blocks) 

, а затем main woul d выглядеть следующим образом:

main = do 
    blocks <- readBlocks 
    putStr $ unlines $ 
    zipWith (\n line -> "Case " ++ show n ++ ": " ++ line) 
      [1..] (map (show . solve . blockPair) blocks) 
+0

Спасибо, твой первый трюк сработал. Но что касается второго, у меня есть аналогичная функция, которая заканчивается на вводе 0, но как я могу контролировать параметры, передаваемые моей выходной функции? Ваш код будет обрабатывать каждую строку как отдельный параметр, который я не хочу. В любом случае, спасибо за попытку. – user7201762

+1

Да, теперь ваш подход более ясен. Это именно то, чего я хочу. Я думаю, проблема теперь решена. Спасибо. – user7201762

+0

@ user7201762, если этот ответ сделал трюк, вы можете пометить его как «принятый ответ», нажав галочку в верхней части ответа. – luqui

1

Это похоже по духу ответа К. А. Бур (в решающий шаг еще проходя состояние в качестве параметра), но по-разному учитываться, чтобы продемонстрировать ловкий трюк. Поскольку IO действия просто нормальные значения Haskell, вы можете использовать цикл, чтобы построить действие, которое будет печатать на выходе без его выполнения:

loop :: (Int, IO()) -> IO() 
loop (nCase, prnAccum) = do 
x <- getLine 
if x == "0" 
    then prnAccum 
    else do inpLines <- replicateM (read x) getLine 
      let blocks = map read $ words $ unwords inpLines 
       prnAccumAndNext = do 
        prnAccum 
        putStr $ "Case " ++ show nCase ++ " : output = " 
        print $ solve $ blockPair blocks 
      loop (nCase + 1, prnAccumAndNext) 

main = loop (1, return()) 

Некоторые замечания о решении выше:

  • prnAccum, то действие, которое печатает результаты, пронумеровано через рекурсивные вызовы цикла, как и nCase (я упаковал их как в паре в стиле, но работал бы так же хорошо, если бы они были переданы как отдельные аргументы).
  • Обратите внимание, как обновленное действие, prnAccumAndNext, не находится непосредственно в главном do блок; он определяется в блоке let. Это объясняет, почему это не выполняется на каждой итерации, но только в конце цикла, когда выполняется окончательный prnAccum.
  • Как предполагает luqui, я удалил аннотации типа, которые вы использовали с read. Тот, который на вызове replicateM, безусловно, не нужен, а другой - не так давно, как blockPair принимает список Int в качестве аргумента, как кажется.
  • Nitpicking: Я удалил точки с запятой, поскольку они не нужны. Кроме того, если arr ссылается на «массив», это не очень подходящее имя (так как это список, а не массив), поэтому я взял на себя смелость изменить его на что-то более описательное. (Вы можете найти другие идеи для полезных трюков и корректировок стиля в ответе К. А. Бура.)
Смежные вопросы