简单的Haskell程序行为不正确

时间:2016-06-01 19:27:54

标签: haskell

我是Haskell的新手,并尝试编写简单的程序来查找最大元素,它是来自intput的索引。我收到的值要逐一比较。我在 maxi 变量中持有的最大元素,它是 maxIdx 中的索引。这是我的计划:

loop = do
    let maxi = 0
    let maxIdx = 0
    let idx = 0
    let idxN = 0
    replicateM 5 $ do
        input_line <- getLine
        let element = read input_line :: Int

        if maxi < element
        then do 
            let maxi = element
            let maxIdx = idx
            hPutStrLn stderr "INNER CHECK"
        else
            hPutStrLn stderr "OUTER CHECK"

        let idx = idxN + 1
        let idxN = idx

        print maxIdx

    loop

即使我知道从大到小(5,4,3,2,1)开始的元素一直进入INNER CHECK(它应该只发生在第一个元素!)和 maxIdx < / strong>始终为0。 我究竟做错了什么? 提前谢谢。

2 个答案:

答案 0 :(得分:9)

无论如何,让我们玩得开心。

loop = do
    let maxi = 0
    let maxIdx = 0
    let idx = 0
    let idxN = 0
    replicateM 5 $ do
        input_line <- getLine
        let element = read input_line :: Int

        if maxi < element
        then do 
            let maxi = element
            let maxIdx = idx
            hPutStrLn stderr "INNER CHECK"
        else
            hPutStrLn stderr "OUTER CHECK"

        let idx = idxN + 1
        let idxN = idx

        print maxIdx

    loop

不是特别的Haskelly代码(而且你知道并不是特别正确)。

如果是Haskellier,那就让我们做吧。

我们在这做什么?我们有一个无限循环,它读取一行5次,对它做了些什么,然后再次调用自己没有特别的原因。

让我们把它分开:

import Control.Monad

readFiveLines :: IO [Int]
readFiveLines = replicateM 5 readLn

addIndex :: [Int] -> [(Int, Int)]
addIndex xs = zip xs [0..]

findMaxIndex :: [Int] -> Int
findMaxIndex xs = snd (maximum (addIndex xs))

loop :: ()
loop = loop

main :: IO ()
main = do xs <- readFiveLines
          putStrLn (show (findMaxIndex xs))

snd返回元组中的第二个元素; readLn基本上是read . getLine; zip获取两个列表并返回一对列表; maximum找到最大值。

我保持loop完整无缺的原因。

如果您记得something (huge expression)可以替换为something $ huge expression$只是将其左操作数应用于其右操作数),您甚至可以是Haskellier,并且这些函数可以与.f (g x)(f . g) xf . g $ x相同(请参阅?它也适用于左侧!)。此外,zip x y可以重写为x `zip` y

import Control.Monad

readFiveLines :: IO [Int]
readFiveLines = replicateM 5 readLn

addIndex :: [Int] -> [(Int, Int)]
addIndex = (`zip` [0..])

findMaxIndex :: [Int] -> Int
findMaxIndex = snd . maximum . addIndex

main :: IO ()
main = do xs <- readFiveLines
          putStrLn . show . findMaxIndex $ xs

对于调试打印,有一个名为Debug.Trace的包和一个函数traceShow,它将其第一个参数(格式为show,因此名称)打印到stderr,并返回其第二个参数:

findMaxIndex :: [Int] -> Int
findMaxIndex = snd . (\xs -> traceShow xs (maximum xs)) . addIndex

这允许您点击任何表达式并查看正在发生的事情(以及周围的值是什么 - 您可以show元组,列表等。)

答案 1 :(得分:0)

我认为alf的答案非常好,但是对于它的价值,这就是我如何解释你的意图。

{-# LANGUAGE FlexibleContexts #-}

module Main where

import System.IO
import Control.Monad.State

data S = S { maximum :: Int
           , maximumIndex :: Int
           , currentIndex :: Int }

update :: Int -> Int -> S -> S
update m mi (S _ _ ci) = S m mi ci

increment :: S -> S
increment (S m mi ci) = S m mi (ci+1)

next :: (MonadIO m, MonadState S m) => m ()
next =  do
  S maxi maxIdx currIdx <- get

  input <- liftIO $ getLine
  let element = read input :: Int

  if maxi < element
  then do
    modify (update element currIdx)
    liftIO $ hPutStrLn stderr "INNER CHECK"
  else
    liftIO $ hPutStrLn stderr "OUTER CHECK"

  modify increment

run :: Int -> IO S
run n = execStateT (replicateM_ n next) (S 0 0 0)

main :: IO ()
main = do
  S maxi maxIdx _ <- run 5    
  putStrLn $ "maxi: " ++ (show maxi) ++ " | maxIdx: " ++ (show maxIdx)

这使用monad变换器将有状态计算与IO相结合。 get函数检索当前状态,modify函数允许您更改状态。