我试图从可选参数构建一个字符串。例如,从标题和名称生成问候语字符串这在命令式语言中是微不足道的,看起来像这样
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}"但实际上,模板可以是发票,声明信,甚至是完整的会计报告。
答案 0 :(得分:9)
实际上,Haskell比任何强制方法更聪明。
设想name
的值为Nothing
。渲染像"Hello Mr"
这样的东西是否有意义?将它视为特殊情况可能会更有意义,为此我们可以再次使用Maybe
。首先,我将函数的签名更新为:
greeting :: Bool -> Maybe String -> Maybe String
现在我们可以再次查看我们的问题,并发现Haskell提供了多种方法来处理它。
Maybe
是一个monad,因此我们可以使用do
语法:
greeting mr name = do
nameValue <- name
return $ if mr
then "Hello, Mr. " ++ nameValue
else "Hello, " ++ nameValue
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 ""
如果您有Maybe
个mconcat
,则可以使用String
,因为import Data.Monoid
import Data.Maybe
greeting :: [Maybe String] -> String
greeting l = fromJust $ mconcat ((Just "Hello"):l)
是一个幺半群:
{{1}}
答案 2 :(得分:1)
我认为您的功能违反了SRP(单一责任原则)。它做了两件事:
Mr
您可以注意到Bool
和String
(姓名)指的是同一个“实体”(一个人)。
所以让我们定义一个人:
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"
代码简单易读,只需几行(不要害怕编写代码)。这两个动作(前缀和问候)是分开的,一切都更简单。
如果你真的需要,你可以平凡地创建一个函数来根据布尔值生成Mister
或Person
:
makePerson :: Bool -> String -> Person
makePerson True = Mister
makePerson False = Person
答案 3 :(得分:1)
Haskell非常擅长抽象,它可以轻松地复制命令式模式。您在示例中所做的事情被称为&#34; builder&#34;图案。 Writer
是一个monad,它包含了一种积累模式或者#34; building&#34;任何Monoid
数据,String
是Monoid
。
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"