Haskell:有没有更好的方法来编写具有相同RHS的case语句?

时间:2015-01-31 01:17:16

标签: haskell case

示例:

data A =
  A B D
  | Aa B C
  | Ag B X X
  | Ae B R Q
  | Ax X

getB a = case a of 
    (A b _)         -> b
    (Aa b _)        -> b
    (Ag b _ _)      -> b
    (Ae b _ _)      -> b
    (Ax _)          -> somethingElse

在Haskell中,给定一个数据类型,其中许多构造函数具有相同的参数类型,是否有更好的方法来返回此参数。或者是否有更好的方法来编写上面显示的case语句以减少重复次数?

6 个答案:

答案 0 :(得分:4)

ML中提供的名为"或模式"的功能对此有很大帮助。五年前GHC的这个特征是requested,但似乎没有人承担指定细节和实际实施细节的任务。但是,有一个package提供了一种方法来使用Template Haskell做这样的事情,如Or-patterns in Haskell中所述

答案 1 :(得分:3)

您可以使用Prism(^?) operator中的lens package来简化此操作:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

-- ...

data A =
  A { _b :: B, _d :: D }
  | Aa { _b :: B, _c :: C }
  | Ag { _b :: B, _x1 :: X, _x2 :: X }
  | Ae { _b :: B, _r :: R, _q :: Q }
  | Ax { _x1 :: X }

makeLenses ''A

getB :: A -> B
getB a = case a ^? b of
  Just theB -> theB
  Nothing   -> somethingElse

对模板Haskell函数makeLenses的调用负责所有样板。

lens可能有点依赖,如果您只是为此而使用它,但需要考虑(特别是如果您已经在使用lens /考虑使用{{1} })。

答案 2 :(得分:3)

你可以使用记录语法来解决这个问题:

data A =
  A {fieldB :: B, fieldC :: C} |
  Aa {fieldB :: B, fieldX1 :: X, fieldX2 :: X} |
  Ag {fieldB :: B, fieldR :: R, fieldQ :: Q} |
  Ax {fieldX :: X}

getB a = case a of
  Ax -> somethingElse
  _  -> fieldB a

关键是要为B类型的所有字段提供相同的名称

答案 3 :(得分:2)

函数级别的模式匹配有助于提高可读性,但由于这些都是不同的构造函数,因此无法一次模式匹配多个(我知道)。

getB (A b _)    = b
getB (Aa b _)   = b
getB (Ag b _ _) = b
getB (Ae b _ _) = b
getB (Ax _)     = somethingElse

答案 4 :(得分:1)

如果A有数据实例,您可以写

import Data.Data
mgetB :: A -> Maybe B
mgetB = gmapQi 0 cast

然后根据该函数定义getB

答案 5 :(得分:1)

在某些时候,你必须说明你的B是如何包含在A中的,所以你们很多人一劳永逸地做到这一点。

bOrX a = case a of
    (A b _)         -> B' b
    (Aa b _)        -> B' b
    (Ag b _ _)      -> B' b
    (Ae b _ _)      -> B' b
    (Ax x)          -> X' x

随后您可以使用非常少的代码一次匹配所有B

getB a = case bOrX a of
  B' b -> b
  X' _ -> somethingElse

anotherFunctionWithBandX a = case bOrX a of
  B' b -> f b
  X' x -> g x