检测Haskell中的底部值

scr*_*avy 9 haskell exception-handling

我写了一个haskell函数,它将列表拆分xs(init xs, last xs)如下:

split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, x)
        split' acc (x:xs) = split' (x:acc) xs
Run Code Online (Sandbox Code Playgroud)

由于无法以这种方式拆分空列表,因此空列表不匹配.但是,我不想简单地使用error ...这个功能.因此我定义了以下内容:

split [] = ([], undefined)
Run Code Online (Sandbox Code Playgroud)

由于懒惰的评估,我可以定义一个安全init,它只返回空列表的空列表:

init' = fst . split
Run Code Online (Sandbox Code Playgroud)

如果我试图访问它,有什么方法可以检测到undefined,这样

last' xs
  | isUndefined (snd xs) = ...
  | otherwise            = ...
Run Code Online (Sandbox Code Playgroud)

我不知道MaybeEither的,而那些对于表达我想要的是更好的选择.但是我想知道是否有办法检测未定义的实际值,即捕获错误,如捕获异常.

sha*_*ang 11

undefined并不比使用更好error.事实上,undefinedPrelude被定义

undefined =  error "Prelude.undefined"
Run Code Online (Sandbox Code Playgroud)


现在,一个不能产生a的函数error被称为"总函数",即它对所有输入值都有效.

split您当前实现的功能具有签名

split :: [a] -> ([a], a)
Run Code Online (Sandbox Code Playgroud)

这是一个问题,因为类型签名承诺结果总是包含一个列表和一个元素,这显然不可能为泛型类型的空列表提供.

Haskell中用于解决此问题的规范方法是更改​​类型签名,以表示有时我们没有第二个项的有效值.

split :: [a] -> ([a], Maybe a)
Run Code Online (Sandbox Code Playgroud)

现在,您可以为获得空列表的情况编写正确的实现

split [] = ([], Nothing)
split xs = split' [] xs
    where
        split' acc (x:[]) = (reverse acc, Just x)
        split' acc (x:xs) = split' (x:acc) xs
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过模式匹配来检测缺失值大小写

let (init', last') = split xs
in case last' of
    Nothing -> ... -- do something if we don't have a value
    Just x  -> ... -- do something with value x
Run Code Online (Sandbox Code Playgroud)

  • 有点讽刺的是未定义的定义;) (5认同)
  • @DanBurton嗯,如果这是你的幽默,请注意也可以像这样定义`undefined`:`undefined = undefined`.它的行为不同(此定义在强制时不会终止),但从语义上讲,它具有相同的值(底部). (2认同)

Ing*_*ngo 10

因为底部包含非终止,所以该函数isUndefined必须解决暂停问题,因此不能存在.

但请注意,即使它存在,您仍然无法判断元组的第二个元素中的未定义值是否通过您的split函数放在那里,或者列表的最后一个元素是否已经未定义.


Sjo*_*her 8

error评估之前,该函数不会执行任何操作,因此您可以执行以下操作:

split [] = ([], error "split: empty list")

last' = snd . split
Run Code Online (Sandbox Code Playgroud)