我有一个函数调用另一个函数,它可以根据某些条件返回一些东西或什么都没有。我想知道的是,如果它什么都不返回,我怎么用不同的参数再次调用它。
例如,我说有一个函数可以以30分钟的间隔返回时间,而另一个函数只能在某个时间没有返回任何内容,即:certainTimeFunction :: (Int,Int,Int) -> Maybe String
certainTimeFunction (h,m,s) =
| (11,00,00) = Just "It's eleven o'clock"
| otherwise = Nothing
timeFunction :: (Int,Int,Int) -> (Int,Int,Int)
timeFunction time = certainTimeFunction time
时间必须从(00,00,00)开始,当然会返回Nothing但是然后(00,30,00)尝试再次返回Nothing直到(11,00,00)然后触发函数为了返回第一个守卫中的内容,整个周期应该结束。
答案 0 :(得分:6)
与Haskell一样,答案是使用递归!
timeFunction :: (Int, Int, Int) -> (Int, Int, Int)
timeFunction time0 = case certainTimeFunction time0 of
Nothing -> timeFunction (perturbTime time0)
Just res -> res
请注意,perturbTime
和certainTimeFunction
应该仔细调整以协同工作,因为在这样的情况下构建无限循环非常容易。更简洁的方法是使用iterate
将递归“展开”到一个惰性列表中。
首先,我们创建一个“所有扰动”的无限列表
... iterate perturbTime :: [(Int, Int, Int)]
然后我们map
certainTimeFunction
检查是否有任何此类扰动返回Just
。
... map certainTimeFunction . iterate perturbTime :: [Maybe (Int, Int, Int)]
然后我们可以通过删除所有timeFunction
来恢复Nothing
的确切行为。
timeFunction = fromJust . head . dropWhile isNothing
. map certainTimeFunction
. iterate perturbTime
我们还可以创建timeFunction
的版本,如果perturbTime
未在n
扰动中达到成功点,则会失败
timeFunctionN n = safeJustHead
. dropWhile isNothing
. take n
. map certainTimeFunction
. iterate perturbTime
where safeJustHead :: [Maybe a] -> Maybe a -- note this is just `msum`
safeJustHead [] = Nothing -- it's also easy to implement
safeJustHead (x:_) = x -- using `Data.Maybe.listToMaybe`
-- per @bheklilr's comment below
答案 1 :(得分:2)
如果你只想以30分钟的间隔喂它,直到成功,你可以
import Data.Maybe
timeFunction :: (Int, Int, Int) -> (Int, Int, Int)
timeFunction initialTime = head $ catMaybes $ map certainTimeFunction times
where
add30 (h, m, s) =
let newM = m + 30
newH = h + newM `div` 60
in (newH, newM `mod` 60, s)
times = iterate add30 initialTime
这将懒惰地计算从您的初始时间起30分钟的所有增量(我的add30
是@JAbrahamson的perturbTime
),然后certainTimeFunction
被映射到所有这些时间,然后{{ 1}}将此压缩为catMaybes
值(懒惰地,再次),并且第一个被采用。请注意,如果你没有得到Just
从中返回成功值的时间,这将永远循环!
如果您想在小时数大于23时停止,请使用我们的好朋友certainTimeFunction
并将takeWhile
更改为
times
并将times = takeWhile (\(h', _, _) -> h' < 24) $ iterate add30 initialTime
的类型更改为timeFunction
这是一个很好的例子,说明为什么你应该定义自己的类型而不是使用元组。如果你有
Time -> Maybe String
然后是一些辅助函数
data Time = Time
{ hours :: Int
, minutes :: Int
, seconds :: Int
} deriving (Eq, Show)
可以更清晰地写成
import Data.Function (on)
asSeconds :: Int -> Time
asSeconds x = Time (x `div` 3600) (x `div` 60 `mod` 60) (x `mod` 60)
asMinutes :: Int -> Time
asMinutes = asSeconds . (* 60)
asHours :: Int -> Time
asHours x = asMinutes . (* 60)
toSeconds :: Time -> Int
toSeconds (Time h m s) = h * 3600 + m * 60 + s
(+:) :: Time -> Time
(+:) = asSeconds .: on (+) toSeconds where (.:) = (.).(.)
timeFunction :: Time -> Maybe Time
timeFunction initialTime = listToMaybe $ catMaybes $ map certainTimeFunction times
where
times = takeWhile ((< 23) . hours) $ iterate (+: asMinutes 30) initialTime
您还可以非常轻松地timeFunction = listToMaybe . catMaybes
. map certainTimeFunction
. takeWhile ((< 24) . hours)
. iterate (+: asMinutes 30)
Time
个实例
Ord