如何使用Option.map和Option.bind重写多次空值检查?

时间:2015-05-01 06:44:06

标签: f#

如何将以下使用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有帮助吗?

2 个答案:

答案 0 :(得分:10)

仅供参考F#4已添加Option.ofObj

在F#中void get_total(FILE*fp){ char c; int total=0; int number_of_conversions; number_of_conversions = fscanf(fp, "%c", &c); while (isdigit(c) || isspace(c) && c !='\n' && c !=NULL){ if (isspace(c)){ number_of_conversions = fscanf(fp, "%c", &c); } else if (isdigit(c)){ total = total + (c- '0'); number_of_conversions = fscanf(fp, "%c", &c); } number_of_conversions = fscanf(fp, "%c", &c); } printf("%d", total); 是有充分理由避免的。在处理依赖null的C#库时,我的一般建议是提供一个F#惯用的&#34;适配器&#34;在那个图书馆。

在实践中,这可能是相当多的工作,结果可能不像C#操作符null那样简洁(不论这个操作符是否是个好主意,除了参数)。

据我所知,F#编译器不支持这样的操作符,但是如果你对它有强烈感觉,你应该在http://fslang.uservoice.com/处提高它。 F#社区是友好的,但我怀疑你必须非常积极地争论,以使社区相信它对F#来说是一个好主意。

同时;一种使其更简洁的方法是创建一个computation expression这样的代码(?.就是你的代码的样子):

getAttributeValue

答案 1 :(得分:4)

你可以使用Fsharpx中的monad 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中定义的标准工作流构建器,而不是定义你自己的定制的(你可以从那里复制和粘贴)回答)。