2

我正在使用行功能来获取输入并在将其发送到函数之前拆分许多变量。请查看运行函数并告诉我为什么会出现以下错误。似乎它应该只将 ln 中的第一个字符串分配给 seq,但我得到一个错误。

错误:不诚实.hs:33:11:
    无法将预期类型“[t]”与推断类型“Char”匹配
    在“do”表达式中: seq <- ln !! 0
    在表达式中:
        做 ln <- 行 s
           序列 <- ln !! 0
           状态 <- ln !! 1
           l1 <- listDouble (ln !! 2)
           ……
    在“运行”的定义中:
        运行 s = do ln <- 行 s
                   序列 <- ln !! 0
                   状态 <- ln !! 1
                   ……
代码如下...

import Char

maximumInd :: (Double, Double) -> Int
maximumInd (d1,d2) | maximum [d1,d2] == d1 = 1
                   | maximum [d1,d2] == d2 = 2

scoreFunction :: String -> Int -> [Double] -> [Double] -> Double -> Double -> (Double,Double)
scoreFunction string (-1) l1 l2 t1 t2 = (0.5, 0.5)
scoreFunction string index l1 l2 t1 t2 = ((fst (scoreFunction string (index-1) l1 l2 t1 t2)) * (l1!!num) * (tr (maximumInd (scoreFunction string (index-1) l1 l2 t1 t2))!!1), (snd (scoreFunction string (index-1) l1 l2 t1 t2)) * (l2!!num) * (tr (maximumInd (scoreFunction string (index-1) l1 l2 t1 t2))!!2))
    where
        num = digitToInt (string!!index)
        tr n | n == 1 = l1
             | n == 2 = l2

--split is stolen from teh webs http://julipedia.blogspot.com/2006/08/split-function-in-haskell.html
split :: String -> Char -> [String]
split [] delim = [""]
split (c:cs) delim
   | c == delim = "" : rest
   | otherwise = (c : head rest) : tail rest
   where
       rest = split cs delim

readDouble :: String -> Double
readDouble s = read s :: Double

listDouble :: String -> [Double]
listDouble s = map readDouble $ split s ' '

run :: String -> String
run s = do
    ln <- lines s
    seq <- ln!!0
    states <- ln!!1
    l1 <- listDouble (ln!!2)
    l2 <- listDouble (ln!!3)
    tr1 <- readDouble (ln!!4)
    tr2 <- readDouble (ln!!5)
    show maximumInd (scoreFunction seq (length seq) l1 l2 tr1 tr2)

main = do
    putStrLn "Please compose a test job for Viterbi."
    putStrLn "First line: A sequence with language [1,9]."
    putStrLn "Second line: The number of states."
    putStrLn "For the next 2 lines: space delimited emission probabilities."
    putStrLn "For the 2 lines after that, transmission probabilities."
    putStrLn "Then do ./casino < filename "
    interact run
4

5 回答 5

5

首先,让我们看看编译器是如何解释它的:

run :: String -> String

String其实是[Char]

run s = do
    ln <- lines s
    ...

简化了很多事情,一个do块必须在Monad. 这意味着它“返回”一个 type 的值(Monad t) => t a。由于此函数正在返回[Char],因此该do块将返回[Char],即Monad[](如果您阅读[a][] a,会更清楚)。

从我的另一个答案复制,

简化了很多事情,在 IO monad 的 do 块上,每一行都是:

  • 返回“IO a”类型的值的东西;其中“a”类型的值被丢弃(因此“a”通常是“()”)
  • 一个 <- 表达式,它做同样的事情,但不是丢弃“a”类型的值,而是将它的名称放在 <- 的左侧
  • 一个 let,它只不过是给一个值一个名字

在这里,我们不是在IOMonad 上,而是在[]Monad 上。所以右边的表达式<-必须是 a [a]

因此,在该do块的第一行:

    ln <- lines s

这里类型是[[Char]],所以类型ln[Char]

在下一行:

    seq <- ln!!0

这里ln!!0有 type Char,但是由于您在[]Monad 中,因此它需要某种列表。这就是导致编译器错误消息的原因。

do解决方案是使用普通let块,而不是使用符号:

run :: String -> String
run s = let
        ln = lines s
        seq = ln!!0
        states = ln!!1
        l1 = listDouble (ln!!2)
        l2 = listDouble (ln!!3)
        tr1 = readDouble (ln!!4)
        tr2 = readDouble (ln!!5)
    in show maximumInd (scoreFunction seq (length seq) l1 l2 tr1 tr2)

我没有编译这个块,但即使它有其他问题,也应该足以让你重新开始。

于 2008-11-16T03:06:10.710 回答
0

请记住,列表是 haskell 中的 monad,其定义如下:

instance Monad [] where
    m >>= f  = concatMap f m
    return x = [x]
    fail s   = []

因此,如果您使用代码,则类似于:

do {ln <- lines "hello, world"; ln!!0}

这等效于以下使用绑定表示法:

lines "hello world" >>= (\ln -> ln!!0)

或更简洁地说:

lines "hello world" >>= (!!0)

我们现在可以使用 list monad 的定义来重写它,如下所示:

concatMap (!!0) (lines "hello, world")

这相当于:

concat $ map (!!0) (lines "hello, world")

"hello, world" 行将返回 ["hello, world"],因此在其上映射 (!!0) 将生成字符串 "h"。它具有类型 [Char],但 concat 需要类型 [[t]]。Char 与 [t] 不匹配,因此出现错误。

尝试使用 let 或其他东西而不是 do 表示法。

编辑:

所以我认为这就是你想要的,使用 let 而不是 do。

run :: String -> String
run s = let ln = lines s
            seq = ln!!0
            states = ln!!1
            l1 = listDouble (ln!!2)
            l2 = listDouble (ln!!3)
            tr1 = readDouble (ln!!4)
            tr2 = readDouble (ln!!5)
        in show $ maximumInd (scoreFunction seq (length seq) l1 l2 tr1 tr2)
于 2008-11-16T02:58:52.100 回答
0

是的,我认为米帕迪是对的。do 表示法转换为 >>= 并返回对列表 monad 的调用。

run s = do
    ln <- lines s
    seq <- ln!!0
    states <- ln!!1

将获得由lines sseq 和 states 返回的列表,ln 每次都是该列表的字符串。所以实际上使用 ln!!0,你会得到那个字符串的第一个字符。但是那里的右侧需要一个列表<-。这就是我记得的所有内容。自从我用haskell做这些事情以来已经有很长一段时间了:)

于 2008-11-16T02:59:34.390 回答
0

我不确定这是否正确,但问题可能在于它<-不是赋值运算符,因为您似乎正在使用它;它本质上是从一个 monad 中解包一个值。但我不确定这是否是您的问题的原因。

于 2008-11-16T02:39:32.903 回答
0

run 的类型是 String -> String,所以你可能不想要 do 表示法 [1]。我建议你这样做:

  1. 注释掉 listDouble 函数下面的所有内容,加载它,并确保它会编译。
  2. 添加一个格式与您期望的文件类似的测试值。就像是:

     t = "[1,9]\n3\n1.0 1.0 1.0\n1.0 1.0 1.0\n1.0\n1.0"  
    
  3. 为您在运行中定义的值添加顶层测试值

    ln = lines t
    seq = ln!!0
    states = ln!!1
    l1 = listDouble (ln!!2)
    l2 = listDouble (ln!!3)
    tr1 = readDouble (ln!!4)
    tr2 = readDouble (ln!!5)   
    
  4. 使用 scoreFunction 的类型签名来指导您构建该函数的参数,然后是运行的其余部分,最后是 main。

学习使用解释器,例如 Hugs、ghci。学习 :r 和 :t 命令。例如(我正在使用柯里化来给出一些但不是全部的函数参数):

  :t scoreFunction
  :t scoreFunction ""
  :t scoreFunction 3445

您可以使用它让系统帮助您确定您是否在正确的轨道上。在顶层执行此操作会引入与 Prelude.seq 函数的冲突 - 重命名您的 seq,或将您的引用为 Main.seq。

Haskell 因初学者难以理解的错误消息而臭名昭著,因此我建议定期回滚到可编译的版本,方法是注释掉您当前的实验(这是我在上面的步骤 1 中让您做的),或者使用您的编辑撤消功能。

[1]我说可能是因为作为字符列表的字符串是 Monad 类的实例,但这是相当先进的

于 2008-11-16T04:03:03.327 回答