我常常具有这种模式的功能:
f :: a -> b
f x = case x of
... -> g ...
... -> g ...
...
... -> g ...
where g = ...
几乎这种情况有一个语法糖:
f :: a -> b
f ... = g ...
f ... = g ...
...
f ... = g ...
不幸的是我无法将where
附加到它上面:我显然会得到not in scope
一堆。
我可以使g
成为一个单独的函数,但它并不好:我的模块命名空间将被实用程序函数污染。
有没有解决方法?
答案 0 :(得分:10)
不,没有解决方法。如果您有类似函数的多个子句,则它们不能共享where
- 子句。您唯一的选择是使用案例陈述,或执行以下操作:
f x =
go x
where
go ... = g ...
go ... = g ...
g = ...
...如果确实出于某种原因想要使用函数表单。
答案 1 :(得分:10)
我认为你的第一个例子根本不是坏事。唯一的句法权重是case x of
,加上->
而不是=
;后者被您可以省略每个子句的函数名称这一事实所抵消。实际上,即使是dflemstr提出的go
辅助函数在语法上也更重。
不可否认,与普通函数子句语法相比,它略有不一致,但这可能是一件好事:它可以更准确地直观地界定x
可用的范围。
答案 2 :(得分:6)
f = g . h -- h is most of your original f
where h ... = ...
h ... = ...
g =
答案 3 :(得分:5)
从Haskell 2010开始,或者使用GHC,你也可以这样做:
f x
| m1 <- x = g
| m2 <- x = g
...
where g =
但请注意,您无法使用g
中模式中绑定的变量。它相当于:
f x = let g = ... in case () of
() -> case x of
m1 -> g
_ -> case x of
m2 -> g
....
答案 4 :(得分:3)
您的原始解决方案似乎是最好的解决方法。从语法上来说,如果不是更轻,它就不会比函数参数上的直接模式匹配更重。
但是,如果您需要的只是检查先决条件而不是模式匹配,请不要忘记保护,这样您就可以自由地访问where
范围。但实际上我在case of
解决方案中没有看到任何不好的内容。
f :: a -> b
f a
| a == 2 = ...
| isThree a = ...
| a >= 4 = ...
| otherwise = ...
where isThree x = x == 3
答案 5 :(得分:1)
假设您在案例陈述的大多数(如果不是全部)不同分支中始终使用g
,这是否安全?
假设某些f :: a -> b
和a
b
(可能是多态的)g
,c -> d
必然是c
形式的某种函数,表示必须是一种从a
中一致地提取getC :: a -> c
的方法。称之为h . g . getC
。在这种情况下,解决方案是在所有情况下使用h :: d -> b
,c
。
但是假设您无法始终从a
中获取a
。也许f c
的格式为f
,其中Functor
是fmap g :: f c -> f d
?然后你可以f d
,然后以某种方式将b
转换为fmap
。
这里只是漫无目的,但当我看到你似乎在每个分支上都应用g
时,{{1}}是第一个想到的东西。
答案 6 :(得分:1)
使用LambdaCase
,您也可以执行此操作:
{-# language LambdaCase #-}
f :: a -> b
f = \case
... -> g ...
... -> g ...
...
... -> g ...
where g = ...