我是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。 我究竟做错了什么? 提前谢谢。
答案 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) x
或f . 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
函数允许您更改状态。