说我有这样的傻话:
data SomeType
= Unary Int
| Associative SomeType
| Binary SomeType SomeType
some_func :: SomeType -> Int
some_func s =
case s of
Unary n -> n
Associative s1 -> some_func s1
Binary s1 s2 -> some_func s1 + some_func s2
这里some_func将查看给定SomeType中的所有SomeTypes,并总结所有Unary数据构造函数的Ints。 SomeType是一种递归数据类型。
在这些模式匹配中,我重复some_func s1
。有没有办法使用@,when,let或其他任何东西来声明sf1 = some_func s1
并在两者中使用它?像这样:
data SomeType
= Unary Int
| Associative SomeType
| Binary SomeType SomeType
some_func :: SomeType -> Int
some_func s =
case s of
Unary n -> n
Associative s1 -> sf1
Binary s1 s2 -> sf1 + sf2
where
sf1 = some_func s1
sf2 = some_func s2
这里的问题是s1和s2仅在->
之后的块中已知,并且无法计算sf1。
答案 0 :(得分:7)
这不能回答问题,但可以解决问题:
{-# LANGUAGE DeriveFoldable #-}
module SomeType where
import Data.Foldable as F
data SomeType a
= Unary a
| Associative (SomeType a)
| Binary (SomeType a) (SomeType a)
deriving (Foldable)
some_func :: SomeType Int -> Int
some_func = F.foldl1 (+)
答案 1 :(得分:5)
答案是否定的:s1
中的Associative
与s1
中的Binary
不同。它们具有相同名称的事实是一种分心,因为它们存在于不同的环境中。
我猜你可以通过使用帮助器来保存一些输入,但这并没有真正帮助封装重复的逻辑:
some_func :: SomeType -> Int
some_func s = go s
where go (Unary n) = n
go (Associative s1) = go s1
go (Binary s1 s2) = go s1 + go s2
答案 2 :(得分:5)
滥用记录语法!
data SomeType
= Unary { u :: Int }
| Associative { s1 :: SomeType }
| Binary { s1, s2 :: SomeType }
someFunc :: SomeType -> Int
someFunc s = case s of
Unary{} -> u s
Associative{} -> sf1
Binary{} -> sf1 + sf2
where
sf1 = someFunc (s1 s)
sf2 = someFunc (s2 s)
请注意,允许相同类型的不同构造函数在其记录中包含相同的命名字段。如果你走到sf2
分支,懒惰会阻止你在Associative
上发生错误。
答案 3 :(得分:4)
我不确定在这种特定情况下是否会缩短它,但在更一般的情况下,你应该查看Scrap Your Boilerplate。例如:
{-# LANGUAGE Rank2Types, DeriveDataTypeable, NoMonomorphismRestriction #-}
import Data.Generics
data SomeType
= Unary Int
| Associative SomeType
| Binary SomeType SomeType
deriving (Data, Typeable, Show)
flatten_sometype x@(Unary _) = x
flatten_sometype (Associative s) = s
flatten_sometype (Binary (Unary a) (Unary b)) = Unary $ a + b
some_func2 = let Unary n = everywhere (mkT flatten_sometype)
in n
正如您所看到的,通过使用everywhere
,我只需要指定一个本地转换 - SYB负责所有的递归。这真的很有用的地方是你涉及多种类型的时候; SYB很乐意通过您不转换的类型并转换它们的参数。但要小心你使用它多少 - 如果过度使用会导致大量的GC流失。
答案 4 :(得分:0)
编写它的最短方式应该只是模式匹配:
some_func :: SomeType -> Int
some_func (Unary n) = n
some_func (Associative s) = some_func s
some_func (Binary s1 s2) = (some_func s1) + (some_func s2)
仍有重复的部分,所以它可能无法回答你的问题......也许涉及用some_func
来定义fmap some_func
?