是否可以以无点形式编写模式匹配函数?

时间:2017-10-03 23:01:27

标签: haskell

考虑以下Haskell代码。

data Keypress = Keypress Int Char

getSeq :: Keypress -> [Char]
getSeq (Keypress i c) = replicate i c

有没有办法以无点形式写getSeq

getSeq的定义与其模式匹配非常相似,似乎可能有某种方式使用currying或monads或其他东西来避免指定i和{{1}参数。但是,pointfree.io无法解析c的无点输出,我认为由于模式匹配。

有可能吗?

3 个答案:

答案 0 :(得分:16)

通常有用,特别是对于具有多个构造函数的递归数据类型,定义 catamorphism ,这是在为每个可能的构造函数赋予一个函数时折叠数据结构的方法。

例如,对于Bool,catamorphism是

bool :: a -> a -> Bool -> a
bool x _ False = x
bool _ y True = y

Either则是

either :: (a -> c) -> (b -> c) -> Either a b -> c
either f g (Left x) = f x
either f g (Right x) = g x

更高级的catamorphism是列表之一,您之前可能已经看过:foldr

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f init [] = init
foldr f init (x:xs) = f x (foldr f init xs)

我们通常不会这样想(或者至少我没有),但是foldr是一种变形:它处理模式匹配并递归地为你解构列表,只要你为[a]的两个构造函数中找到的值提供“处理程序”:

  • 处理[]的一个案例,根本不需要任何参数:只是b类型的值
  • 处理(x:xs)的一个案例。这种情况采用一个表示x的参数,列表的头部,以及一个表示递归折叠尾部的结果的参数,类型为b的值。

对于只有一个构造函数的类型,catamorphism不那么令人兴奋,但我们可以轻松地为Keypress类型定义一个:

key :: (Int -> Char -> a) -> Keypress -> a
key f (Keypress x c) = f x c

在某种程度上,catamorphism允许您抽象出函数定义的模式匹配部分,之后您可以使用不再需要直接触及基础数据类型的函数。

一旦定义了这个通常有用的功能,你可以多次使用它来实现你心中所需的任何无点功能。在你的情况下,你可以简单地写

getSeq :: Keypress -> [Char]
getSeq = key replicate

答案 1 :(得分:12)

如上所述,没有。但如果你愿意,你可以解决这个问题:

data Keypress = Keypress
  { count :: Int
  , char :: Char }

然后

getSeq p = replicate (count p) (char p)

可以转换为

getSeq = replicate <$> count <*> char

答案 2 :(得分:5)

如果没有一点样板,你就无法做到这一点,但是lens可以为你生成那个样板,所以这可能就像你要得到的那样接近。

使用makePrisms中的Control.Lens.TH将生成Iso,这实际上是单构造函数数据类型上的第一类模式匹配。方便的是,Iso是双向的,因此您可以view它们(以获取值,如模式匹配)和review它们(将值重新放入,就像使用构造函数一样)正常)。

使用makePrismsview,可以以无点的方式编写getSeq

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data Keypress = Keypress Int Char
makePrisms ''Keypress

getSeq :: Keypress -> [Char]
getSeq = uncurry replicate . view _Keypress

这样更好吗?不知道。您的代码中的模式匹配对我来说很合适,但如果您已经使用lens,那么此版本可能更适合您。