我收到2个字符串路径作为参数:input
和output
我希望从input
路径读取文件并将其写入{{1路径。
我想要处理有关输入/输出路径的所有4个场景。当其中一个为null时,我想给它一个默认值。是否有类似于coalesce运算符的东西?我不想为所有场景重写do子句: / p>
方案
output
我想达到什么-do子句:
func null _ -> {do clause}
_ null -> {do clause}
_ _ -> {do clause}
x y -> {do clause}
let defaultInPath="inPath.txt"
defaultOutPath="outPath.txt"
P.S我是Haskell的新手,我真的想要掌握它。
答案 0 :(得分:2)
Maybe
类型构造函数首先,编码你的" null"使用maybe
正确填充字符串。然后,如果参数为Nothing
,则使用func :: Maybe String -> Maybe String -> IO String
func inFile outFile = do
text <- readFile $ maybe defaultIn id inFile
writeFile (maybe defaultOut id outFile) text
return text
函数返回默认值。
Data.Maybe
fromMaybe d = maybe d id
如果您不介意额外导入,可以使用import Data.Maybe
func :: Maybe String -> Maybe String -> IO String
func inFile outFile = do
text <- readFile $ fromMaybe defaultIn inFile
writeFile (fromMaybe defaultOut outFile) text
return text
。
??
?? :: Maybe String -> String -> String
(??) = flip fromMaybe
-- a ?? b = fromMaybe b a
-- a ?? b = maybe b id a
无论哪种方式,您都可以从以下任一函数定义自己的合并运算符:
func inFile outFile = do
text <- readFile (inFile ?? defaultIn)
writeFile (outFile ?? defaultOut) text
return text
并写
Maybe
Maybe String
假设您还没有,那么您的四种类型的电话会是这样的
从返回func Nothing Nothing
func (Just "input.txt") Nothing
func Nothing (Just "output.txt")
func (Just "input.txt") (Just "output.txt")
值的函数中获取值。
{{1}}
答案 1 :(得分:2)
此处已有的其他答案比以下内容更实用,但如果您对更具概念性的事物观点感兴趣,请继续阅读。
首先,Haskell没有空引用,但如果要对缺失值建模,可以使用Maybe
。例如,如果您想将空字符串视为缺失值,则可以编写如下转换函数:
maybeFromNull :: Foldable t => t a -> Maybe (t a)
maybeFromNull xs = if null xs then Nothing else Just xs
你这样使用它:
*Q49616294> maybeFromNull "foo"
Just "foo"
*Q49616294> maybeFromNull ""
Nothing
当谈话落在Haskell中的空合并运算符时,这很有趣的原因是there's a monoid over Maybe that corresponds to that。它被称为First
,它从一系列候选者中返回最左边的非Nothing
值。
由于以后可能会更清楚的原因,我会使用Data.Semigroup
中的那个,所以
import Data.Semigroup
为了使Monoid
行为超过Maybe
,您需要将First
值包含在Option
中; e.g:
*Q49616294> (Option $ Just $ First 42) <> (Option $ Just $ First 1337)
Option {getOption = Just (First {getFirst = 42})}
当然,选择最左边的值是一种啰嗦的方式,但突出显示null coalesce'只是'幺半群:
*Q49616294> (Option $ Just $ First 42) <> (Option Nothing)
Option {getOption = Just (First {getFirst = 42})}
*Q49616294> (Option Nothing) <> (Option $ Just $ First 1337)
Option {getOption = Just (First {getFirst = 1337})}
由于实际使用过于冗长,您可以决定编写一个自定义运算符,将Maybe
值重新打包为Option First
值,应用<>
运算,然后从中取出结果Option First
返回Maybe
:
(<?>) :: Maybe a -> Maybe a -> Maybe a
mx <?> my =
let ofx = Option $ sequenceA $ First mx
ofy = Option $ sequenceA $ First my
leftmost = ofx <> ofy
in getFirst $ sequenceA $ getOption $ leftmost
虽然您可以将此运算符编写为一个大表达式,但我选择使用let...in
语法来“显示我的工作”。
但仍存在一个问题:
*Q49616294> Just 42 <?> Just 1337
Just 42
*Q49616294> Nothing <?> Nothing
Nothing
虽然只要至少有一个参数是Just
值,操作就会返回Just
值,但可以返回Nothing
。
如何应用后备值以确保在所有情况下都能获得值?
您可以利用Option
Foldable
,然后折叠<>
- 仅此时,正在使用另一个Monoid
实例:
(<!>) :: Maybe a -> a -> a
mx <!> y =
let ofx = Option $ sequenceA $ First mx
fy = First y
in getFirst $ foldr (<>) fy ofx
此运算符使用ofx
作为初始值折叠fy
。此处,<>
属于First
Semigroup
,它无条件地返回最左边的值。此处不涉及Option
,因为foldr
剥离了该层。但是,由于我们从右侧折叠,如果fy
包含值,则始终会忽略初始值ofx
。
*Q49616294> Just 42 <!> 1337
42
*Q49616294> Nothing <!> 1337
1337
您现在可以按如下方式编写所需的功能:
copyFile :: String -> String -> IO String
copyFile input output = do
text <- readFile $ (maybeFromNull input) <!> defaultInPath
writeFile (maybeFromNull output <!> defaultOutPath) text
return text
事实证明,在这种情况下,您甚至不需要<?>
,但在其他情况下,您可以使用此运算符链接任意数量的潜在值:
*Q49616294> Just 42 <?> Nothing <?> Just 1337 <!> 123
42
*Q49616294> Nothing <?> Nothing <?> Just 1337 <!> 123
1337
*Q49616294> Nothing <?> Nothing <?> Nothing <!> 123
123
这种实现空合并行为的方式不仅不必要地复杂化,如果它不能很好地加以伤害,我也不会感到惊讶。
然而,它确实说明了Haskell内置抽象的力量和表现力。答案 2 :(得分:1)
如果您有可能提供或未提供的值,您应该使用Maybe安全,灵活地对此进行编码。
但是,如果您真的想要替换空字符串或任何其他魔术值,您可以轻松地使用if..then..else
作为表达式:
func :: String -> IO ()
func input = do
text <- readFile (if input == "" then defaultIn else input)
putStrLn text
当然,一旦你切换到Maybe并发现自己有一个普通的字符串,你可以使用它来调用它:
func :: Maybe String -> IO ()
func input = do
text <- readFile $ fromMaybe "default.txt" input
putStrLn text
main = do
putStrLn "Enter filename or blank for default:"
file <- getLine
func (if file == "" then Nothing else Just file)