如何在haskell中链接使用可能的参数?

时间:2014-05-23 21:34:19

标签: haskell

我试图从可选参数构建一个字符串。例如,从标题和名称生成问候语字符串这在命令式语言中是微不足道的,看起来像这样

def greeting(title, name):
   s = "Hello"
   if a :
        s += "Mr"
   if b: 
        s += b

我在haskell的第一次尝试是:

greeting :: Bool-> Maybe String -> String
greeting title name = foldl (++) "Hello" (catMaybes [title' title, name])
   where
   title' True = Just "Mr"
   title' False = Nothing

我确信有更好的方法可以做到。首先,我确定这个foldl catMaybes组合存在于某个地方,但我无法找到它。其次,折叠在这里工作,因为我使用相同的操作(和相同的类型)。那么有什么更好的方法呢? 我也在考虑使用作家,但我不知道该怎么做。

更新

这只是一个例子(可能不好或太简单)。我的问题更多,如何将其推广到许多论点。

所以真正的问题不只是关于连接2个字符串,而是关于如何使用可选的部分和参数从模板生成字母,就像使用邮件合并工具在 Word 中一​​样。

你有一个csv文件,其中包含客户名称,电话号码,逾期等等。某些字段可以是可选的。另一方面,您有一个模板,目标是根据*模板为每个客户(行)生成一个字母。那么问题是你如何在haskell中编写这个模板(没有任何模板库的帮助)。 在我的第一个例子中,模板将是" hello(Mr){name}"但实际上,模板可以是发票,声明信,甚至是完整的会计报告。

5 个答案:

答案 0 :(得分:9)

实际上,Haskell比任何强制方法更聪明。

设想name的值为Nothing。渲染像"Hello Mr"这样的东西是否有意义?将它视为特殊情况可能会更有意义,为此我们可以再次使用Maybe。首先,我将函数的签名更新为:

greeting :: Bool -> Maybe String -> Maybe String

现在我们可以再次查看我们的问题,并发现Haskell提供了多种方法来处理它。

您好,Monads

Maybe是一个monad,因此我们可以使用do语法:

greeting mr name = do
  nameValue <- name
  return $ if mr
    then "Hello, Mr. " ++ nameValue
    else "Hello, " ++ nameValue

你好,Functors

Maybe也是一个仿函数,所以我们也可以使用fmap

greeting mr name = 
  if mr
    then fmap ("Hello, Mr. " ++) name
    else fmap ("Hello, " ++) name

如果我们将签名视为以下内容,我们也可以进行一些重构:

greeting :: Bool -> (Maybe String -> Maybe String)

即,作为一个参数的函数,它返回另一个函数。所以实现:

greeting mr = 
  if mr
    then fmap ("Hello, Mr. " ++)
    else fmap ("Hello, " ++)

greeting True  = fmap ("Hello, Mr. " ++)
greeting False = fmap ("Hello " ++)

如果你发现这种语法更好。

答案 1 :(得分:1)

您可以避免使用Maybe作为标题并执行:

greeting :: Bool-> Maybe String -> String
greeting title name = "Hello" ++ title' ++ (maybe "" id name)
  where title' = if title then "Mr" else ""

如果您有Maybemconcat,则可以使用String,因为import Data.Monoid import Data.Maybe greeting :: [Maybe String] -> String greeting l = fromJust $ mconcat ((Just "Hello"):l) 是一个幺半群:

{{1}}

答案 2 :(得分:1)

我认为您的功能违反了SRP(单一责任原则)。它做了两件事:

  • 使用Mr
  • 为名称添加前缀
  • 呈现问候语消息

您可以注意到BoolString(姓名)指的是同一个“实体”(一个人)。

所以让我们定义一个人:

data Person 
    = Mister String
    | Person String
    deriving (Eq)

我们现在可以创建一个有意义的show实例:

instance Show Person where
    show (Mister name) = "Mr. " ++ name
    show (Person name) = name

最后我们可以将你的问候语重新表述为:

greeting :: Maybe Person -> String
greeting (Just person) = "Hello, " ++ show person
greeting Nothing       = "Hello"

Live demo

代码简单易读,只需几行(不要害怕编写代码)。这两个动作(前缀和问候)是分开的,一切都更简单。


如果你真的需要,你可以平凡地创建一个函数来根据布尔值生成MisterPerson

makePerson :: Bool -> String -> Person
makePerson True  = Mister
makePerson False = Person

Live demo

答案 3 :(得分:1)

Haskell非常擅长抽象,它可以轻松地复制命令式模式。您在示例中所做的事情被称为&#34; builder&#34;图案。 Writer是一个monad,它包含了一种积累模式或者#34; building&#34;任何Monoid数据,StringMonoid

import Control.Monad.Writer hiding (forM_)
import Data.Foldable

greeting :: Bool -> Maybe String -> String -> String
greeting mr name surname = 
  execWriter $ do
    tell $ "Hello,"
    when mr $ tell $ " Mr."
    forM_ name $ \s -> tell $ " " ++ s
    tell $ " " ++ surname
    tell $ "!"

main = do
  putStrLn $ greeting False (Just "Ray") "Charles"
  putStrLn $ greeting True Nothing "Bean"

输出:

Hello, Ray Charles!
Hello, Mr. Bean!

答案 4 :(得分:1)

我知道这是一个旧帖子...但是它可能有用...

以纯粹的“ 功能 ”方式...

greeting :: Maybe String -> String -> String
greeting Nothing name  = "Hello" ++ ", " ++ name
greeting (Just title) name  = "Hello" ++ ", " ++ title ++ " " ++ name ++ "!" 

how to call : 
-- greeting Nothing "John"
-- greeting Nothing "Charlotte"
-- greeting (Just "Mr.") "John"
-- greeting (Just "Miss.") "Charlotte"