繁体   English   中英

通过行的Haskell字符串列表

[英]Haskell string list through lines

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

ERROR:dishonest.hs:33:11:
    Couldn't match expected type `[t]' against inferred type `Char'
    In a 'do' expression: seq <- ln !! 0
    In the expression:
        do ln <- lines s
           seq <- ln !! 0
           states <- ln !! 1
           l1 <- listDouble (ln !! 2)
           ....
    In the definition of `run':
        run s = do ln <- lines s
                   seq <- ln !! 0
                   states <- ln !! 1
                   ....
code follows...

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

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

run :: String -> String

String实际上是[Char]

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

要简化很多事情, do块必须在Monad “运行”。 这意味着它“返回”类型(Monad t) => ta 由于此函数返回[Char] ,因此do块将返回[Char] ,这意味着Monad[] (如果将[a]读为[] a ,则会更清楚)。

从我的另一个答案中复制,

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

  • 返回“ IO a”类型值的东西; 其中的“ a”类型的值将被丢弃(因此,“ a”通常为“()”)
  • 一个<-表达式,它执行相同的操作,但不是丢弃“ a”类型的值,而是为其命名为<-左侧
  • 一个let,它只给值命名

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

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

    ln <- lines s

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

在下一行:

    seq <- ln!!0

此处ln!!0类型为Char ,但是由于您位于[] Monad中,因此期望使用某种列表。 这就是导致编译器错误消息的原因。

解决方案是使用普通的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)

我没有编译此块,但是即使它有其他问题,也应该足以使您重新开始。

我不确定这是否正确,但是问题可能出在以下事实上: <-不是赋值运算符,因为您似乎正在使用它。 它本质上是从monad解包一个值。 但是,我不确定这是否是造成您问题的原因。

请记住,列表是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)

现在,我们可以使用列表monad的定义将其重写为以下内容:

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

等效于:

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

“ hello,world”行将返回[“ hello,world”],因此在其上映射(!! 0)将产生字符串“ h”。 它的类型为[Char],但是concat的类型为[[t]]。 字符不匹配[t],因此错误。

尝试使用let或其他方式,而不要使用not表示法。

编辑:

所以我认为这是您想要的,使用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)

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

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

将获得由lines s返回的列表,对于seq和状态,ln每次都是该列表的字符串。 因此实际上使用ln !! 0,您将获得该字符串的第一个字符。 但是在<-的右侧需要一个列表。 这就是我所记得的一切。 自从我用haskell做这些东西以来已经有很多时间了:)

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命令。 例如(我正在使用currying给出一些但不是全部的函数参数):

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM