如何使用 Option.map 和 Option.bind 重写多个空检查?
How to use Option.map and Option.bind to rewrite the multiple null checks?
如何将以下使用c# HtmlAgility库的代码转换为优雅风格?
if node <> null then
let nodes = node.SelectNodes("//input[@name='xxx']")
if nodes <> null then
let first = nodes.[0]
if first <> null then
let value = first.Attributes.["value"]
if value <> null then
Some value.Value
else
None
else
None
else
None
else
None
下面的代码可以工作吗?但是,它仍然不如 C# 6 的 ?.
运算符简洁。
let toOpt = function null -> None | x -> Some x
node |> toOpt
|> Option.map (fun x -> x.SelectNodes("//input[@name='xxx']"))
|> Option.map (fun x -> x.[0] )
|> Option.map (fun x -> x.Attributes.["value"] )
|> Option.map (fun x -> x.Value )
C#6版本还是简洁多了:
node?.SelectNodes("//input[@name='xxx']")[0]?.Attributes["value"]?.Value
Option.bind
有帮助吗?
FYI F#4 添加了 Option.ofObj
在 F# 中,null
被避免是有充分理由的。在处理依赖于 null
的 C# 库时,我的一般建议是在该库上提供 F# 惯用的 "adapter"。
在实践中,这可能需要大量工作,而且结果可能不像 C# 运算符 ?.
那样简洁(暂且不讨论这样的运算符是否是个好主意)。
据我所知,F# 编译器不支持这样的运算符,但如果您强烈支持它,您应该在 http://fslang.uservoice.com/ 提出它。 F# 社区很友好,但我怀疑您将不得不非常激烈地争论才能让社区相信这对 F# 来说是个好主意。
同时;使它稍微更简洁的一种方法是创建一个像这样的 computation expression(getAttributeValue
是您的代码的样子):
// Basically like the classic `maybe` monad
// but with added support for nullable types
module Opt =
let inline Return v : Option<'T> = Some v
let inline ReturnFrom t : Option<'T> = t
let inline ReturnFrom_Nullable ot : Option<'T> =
match ot with
| null -> None
| _ -> Some ot
let inline Bind (ot : Option<'T>) (fu : 'T -> Option<'U>) : Option<'U> =
match ot with
| None -> None
| Some vt ->
let ou = fu vt
ou
let inline Bind_Nullable (vt : 'T) (fu : 'T -> Option<'U>) : Option<'U> =
match vt with
| null -> None
| _ ->
let ou = fu vt
ou
let Delay ft : Option<'T> = ft ()
type OptBuilder() =
member inline x.Return v = Return v
member inline x.ReturnFrom v = ReturnFrom v
member inline x.ReturnFrom v = ReturnFrom_Nullable v
member inline x.Bind (t, fu) = Bind t fu
member inline x.Bind (t, fu) = Bind_Nullable t fu
member inline x.Delay ft = Delay ft
let inline ofObj o =
match o with
| null -> None
| _ -> Some o
open HtmlAgilityPack
let opt = Opt.OptBuilder()
let getAttributeValue (node : HtmlNode) (path : string) : string option =
opt {
let! nodes = node.SelectNodes path
let! node = nodes.[0]
let! attr = node.Attributes.["value"]
return! attr.Value
}
let html = """
<html>
<title>Hello</title>
<body>Yellow <div name='Test' value='Stone'>Div</div></title>
</html>
"""
[<EntryPoint>]
let main argv =
let doc = HtmlDocument ()
doc.LoadHtml html
let r = getAttributeValue doc.DocumentNode "//div[@name='Test']"
printfn "Result: %A" r
0
您可以在 Fsharpx 中使用 maybe monad
maybe {
let! node = toOpt node
let! nodes = toOpt node.SelectNodes("")
let! first = toOpt nodes.[0]
let! value = toOpt first.Attributes.["value"]
return value.Value
}
如果任何一个为空,这将导致 None
,否则将导致 Some value.Value
。
注意 如果你通读一遍,FuleSnabel 的解决方案实际上更好,因为它可以让你摆脱所有地方的 toOpt
,你可以拥有它就是
opt {
let! node = node
let! nodes = node.SelectNodes("")
let! first = nodes.[0]
let! value = first.Attributes.["value"]
return value.Value
}
选择这个而不是那个的唯一原因是,如果您真的只想将您的项目限制在 Fsharpx 中定义的标准工作流构建器,而不是定义您自己的自定义构建器(您可以复制和粘贴)来自那个答案)。
如何将以下使用c# HtmlAgility库的代码转换为优雅风格?
if node <> null then
let nodes = node.SelectNodes("//input[@name='xxx']")
if nodes <> null then
let first = nodes.[0]
if first <> null then
let value = first.Attributes.["value"]
if value <> null then
Some value.Value
else
None
else
None
else
None
else
None
下面的代码可以工作吗?但是,它仍然不如 C# 6 的 ?.
运算符简洁。
let toOpt = function null -> None | x -> Some x
node |> toOpt
|> Option.map (fun x -> x.SelectNodes("//input[@name='xxx']"))
|> Option.map (fun x -> x.[0] )
|> Option.map (fun x -> x.Attributes.["value"] )
|> Option.map (fun x -> x.Value )
C#6版本还是简洁多了:
node?.SelectNodes("//input[@name='xxx']")[0]?.Attributes["value"]?.Value
Option.bind
有帮助吗?
FYI F#4 添加了 Option.ofObj
在 F# 中,null
被避免是有充分理由的。在处理依赖于 null
的 C# 库时,我的一般建议是在该库上提供 F# 惯用的 "adapter"。
在实践中,这可能需要大量工作,而且结果可能不像 C# 运算符 ?.
那样简洁(暂且不讨论这样的运算符是否是个好主意)。
据我所知,F# 编译器不支持这样的运算符,但如果您强烈支持它,您应该在 http://fslang.uservoice.com/ 提出它。 F# 社区很友好,但我怀疑您将不得不非常激烈地争论才能让社区相信这对 F# 来说是个好主意。
同时;使它稍微更简洁的一种方法是创建一个像这样的 computation expression(getAttributeValue
是您的代码的样子):
// Basically like the classic `maybe` monad
// but with added support for nullable types
module Opt =
let inline Return v : Option<'T> = Some v
let inline ReturnFrom t : Option<'T> = t
let inline ReturnFrom_Nullable ot : Option<'T> =
match ot with
| null -> None
| _ -> Some ot
let inline Bind (ot : Option<'T>) (fu : 'T -> Option<'U>) : Option<'U> =
match ot with
| None -> None
| Some vt ->
let ou = fu vt
ou
let inline Bind_Nullable (vt : 'T) (fu : 'T -> Option<'U>) : Option<'U> =
match vt with
| null -> None
| _ ->
let ou = fu vt
ou
let Delay ft : Option<'T> = ft ()
type OptBuilder() =
member inline x.Return v = Return v
member inline x.ReturnFrom v = ReturnFrom v
member inline x.ReturnFrom v = ReturnFrom_Nullable v
member inline x.Bind (t, fu) = Bind t fu
member inline x.Bind (t, fu) = Bind_Nullable t fu
member inline x.Delay ft = Delay ft
let inline ofObj o =
match o with
| null -> None
| _ -> Some o
open HtmlAgilityPack
let opt = Opt.OptBuilder()
let getAttributeValue (node : HtmlNode) (path : string) : string option =
opt {
let! nodes = node.SelectNodes path
let! node = nodes.[0]
let! attr = node.Attributes.["value"]
return! attr.Value
}
let html = """
<html>
<title>Hello</title>
<body>Yellow <div name='Test' value='Stone'>Div</div></title>
</html>
"""
[<EntryPoint>]
let main argv =
let doc = HtmlDocument ()
doc.LoadHtml html
let r = getAttributeValue doc.DocumentNode "//div[@name='Test']"
printfn "Result: %A" r
0
您可以在 Fsharpx 中使用 maybe monad
maybe {
let! node = toOpt node
let! nodes = toOpt node.SelectNodes("")
let! first = toOpt nodes.[0]
let! value = toOpt first.Attributes.["value"]
return value.Value
}
如果任何一个为空,这将导致 None
,否则将导致 Some value.Value
。
注意 如果你通读一遍,FuleSnabel 的解决方案实际上更好,因为它可以让你摆脱所有地方的 toOpt
,你可以拥有它就是
opt {
let! node = node
let! nodes = node.SelectNodes("")
let! first = nodes.[0]
let! value = first.Attributes.["value"]
return value.Value
}
选择这个而不是那个的唯一原因是,如果您真的只想将您的项目限制在 Fsharpx 中定义的标准工作流构建器,而不是定义您自己的自定义构建器(您可以复制和粘贴)来自那个答案)。