在Haskell中使用映射时跳过异常
我有以下代码以字符串形式返回一个周期的长度:在Haskell中使用映射时跳过异常,haskell,error-handling,map,Haskell,Error Handling,Map,我有以下代码以字符串形式返回一个周期的长度: module Main where import Data.List detec ys n | 2*n > (length ys) = error "no cycle" | t == h = (2*n - n) | otherwise = detec ys (n+1) where t = ys !! n h =
module Main where
import Data.List
detec ys n | 2*n > (length ys) = error "no cycle"
| t == h = (2*n - n)
| otherwise = detec ys (n+1)
where
t = ys !! n
h = if n == 0 then ys !! 1 else ys !! (n*2)
f x = detec (show x) 0
answer = map f [1/x|x<-[1..100]]
modulemain其中
导入数据。列表
检测ys n | 2*n>(长度ys)=错误“无周期”
|t==h=(2*n-n)
|否则=检测(n+1)
哪里
t=ys!!N
h=如果n==0,则ys!!1.其他人是谁!!(n*2)
f x=检测(显示x)0
answer=mapf[1/x | x您可以从控件中使用
import Prelude hiding (catch)
import Control.Exception
main = do
print answer `catch` errorMessage
where
errorMessage :: SomeException -> IO ()
errorMessage = putStrLn . ("error: " ++) . show
捕捉SomeException
是草率的,输出是混乱的:
[error: No cycle
(请注意,这也修复了第一个保护中允许!!
抛出异常的错误。)
这允许类似但更灵活的使用,例如:
answer2 = f2 <$> [1/x | x <- [1..100]]
f2 x = detec2 (show x) 0
main = do
forM_ answer2 $
\x -> case x of
Left msg -> putStrLn $ "error: " ++ msg
Right x -> print x
如果您根本不关心错误消息,那么就不要生成它们。一种自然的方法是使用列表,在列表中,您返回空列表,不循环,并使用concatMap
压缩结果:
detec3 :: (Show a) => a -> [Int]
detec3 x = go 0
where go :: Int -> [Int]
go n
| 2*n >= len = []
| t == h = [2*n - n]
| otherwise = go (n+1)
where t = ys !! n
h | n == 0 = ys !! 1
| otherwise = ys !! (n*2)
len = length ys
ys = show x
main = do
print $ concatMap (detec3 . recip) [1..100]
最后,您可能对阅读感兴趣。请不要使用error
来实现预期会出现“错误”结果的逻辑
相反,为什么不返回Maybe n
而不是只返回n
,然后过滤掉Nothing
s
这些变化非常简单:
module Main where
import Data.List
import Data.Maybe
detec ys n | 2*n > (length ys) = Nothing
| t == h = Just (2*n - n)
| otherwise = detec ys (n+1)
where
t = ys !! n
h = if n == 0 then ys !! 1 else ys !! (n*2)
f x = detec (show x) 0
answer = catMaybes $ map f [1/x|x<-[1..100]]
modulemain其中
导入数据。列表
导入数据,也许吧
检测ys n | 2*n>(长度ys)=无
|t==h=Just(2*n-n)
|否则=检测(n+1)
哪里
t=ys!!n
h=如果n==0,则ys!!1否则ys!!(n*2)
f x=检测(显示x)0
你在做什么
仅仅因为某个数字重复,并不意味着你已经找到了一个循环:例如,在334/999=0.334…,这个循环不是(3),而是(334)。此外,依靠浮点计算给你足够精确的数字是不明智的:26的解肯定超出了浮点所能给你的范围
在任何情况下,都有更简单的方法来查找周期。这会在遍历列表时保留以前看到的元素的列表,并在发现当前元素已被看到时返回
findCycle :: Eq a => [a] -> Int
findCycle = findCycle' [] where
findCycle' _ [] = 0
findCycle' k (x:xs) = maybe (findCycle' (x:k) xs) succ $ elemIndex x k
您的算法不完整:它可能并不总是找到最小的循环。该缺陷在此处得到纠正
findCycle :: Eq a => [a] -> Int
findCycle xs = findCycle' xs (tail xs) where
findCycle' (x:xs) (y:_:ys)
| x == y = fromJust (elemIndex x xs) + 1
| otherwise = findCycle' xs ys
这假设它永远不会从列表的末尾跑出来。如果您实现自己的十进制扩展,您可以很容易地确保这是真的。+1用于阻止错误。
。请注意,甚至不可能对照它返回的\u124;
进行检查!如果有经过计算的错误,整个程序将返回未定义。+1对于相同的错误,一个这也不利于数组索引,不过如果你一开始就指出OP的算法是错误的,我会更喜欢它。因为你没有,所以我做了…catMaybes.map==mappaye
实际上,你可以捕捉到“错误”(和模式匹配失败)在IO中。但你永远都不想达到这一点;这不是一个例外,这是一个bug。要澄清两种方法之间的区别,请注意,可能
也是一个monad。MonadError
是一种更通用的方法,它允许你附加错误消息;如果你不关心错误消息,就没有太多的实践cal差异,两者都可以“直接”样式或使用do
表示法编写。
module Main where
import Data.List
import Data.Maybe
detec ys n | 2*n > (length ys) = Nothing
| t == h = Just (2*n - n)
| otherwise = detec ys (n+1)
where
t = ys !! n
h = if n == 0 then ys !! 1 else ys !! (n*2)
f x = detec (show x) 0
answer = catMaybes $ map f [1/x|x<-[1..100]]
findCycle :: Eq a => [a] -> Int
findCycle = findCycle' [] where
findCycle' _ [] = 0
findCycle' k (x:xs) = maybe (findCycle' (x:k) xs) succ $ elemIndex x k
findCycle :: Eq a => [a] -> Int
findCycle xs = findCycle' xs (tail xs) where
findCycle' (x:xs) (y:_:ys)
| x == y = fromJust (elemIndex x xs) + 1
| otherwise = findCycle' xs ys