4

假设我有这样的愚蠢行为:

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 中的所有 SomeType 并总结所有 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 无法计算。

4

5 回答 5

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 (+)
于 2012-04-21T08:20:13.637 回答
5

答案是否定的:s1in与inAssociative不同。它们具有相同名称的事实令人分心,因为它们存在于不同的上下文中。s1Binary

我想您可以通过使用帮助程序来节省一些输入,但这并不能真正帮助封装重复的逻辑:

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
于 2012-04-21T07:26:57.970 回答
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分支,懒惰可以防止你出错。

于 2012-04-21T15:58:48.043 回答
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 流失。

于 2012-04-21T08:11:47.770 回答
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

于 2012-04-21T09:03:23.847 回答