Return 来自 FOR ... IN 扩展递归方法 (F#)?

Return from FOR ... IN in extension recursive method (F#)?

我是 F# 新手。无法处理语法。我需要将递归扩展方法从 C# 转换为 F#。但是无论我做什么,编译器都不喜欢我的代码。

这是我的方法:

 public static class ViewExtensions
    {

        public static UIView FindFirstResponder(this UIView view)
        {
            if (view.IsFirstResponder)
            {
                return view;
            }
            foreach (UIView subView in view.Subviews)
            {
                var firstResponder = subView.FindFirstResponder();
                if (firstResponder != null)
                    return firstResponder;
            }
            return null;
        }
    }

我试过这样的事情:

  type UIKit.UIView with
     member this.FindFirstResponder() =
        let mutable firstResponder = null
        if (this.IsFirstResponder) then this
        else
          for subView in this.Subviews do
             firstResponder <- subView.FindFirstResponder()

但这显然是不正确的,编译器不会跳过这个。有人可以帮忙吗?

F# 没有早期 return(这是 C# 中的 return 关键字所做的)。 F# 中的每个表达式都必须从上到下进行完整计算,并且不能在中间被打断。来自类 C 的语言可能会让人觉得很奇怪,但它实际上是一个特性,相信我。它导致更易于理解、更易于维护的代码。

在 F# 中对迭代建模的自然方式是递归。只要不进行递归调用,它就可以在任何步骤轻松中断。但是您不必在每种情况下都使用裸递归,因为对于许多常见用例,已经存在库函数(它们本身是建立在递归或其他此类函数之上的)。

在这种特殊情况下,感兴趣的函数是 Seq.tryPick。它采用序列和另一个函数,return 是一个 option - SomeNone。然后 Seq.tryPick 将 return 它遇到的第一个 Some,或者如果序列中没有 SomeNone。要从可为空的 .NET 值转换为 option,请使用 Option.ofObj,转换回来 - 使用 Option.toObj.

  type UIKit.UIView with
     member this.FindFirstResponder() =
        if this.IsFirstResponder then this
        else
          this.Subviews
          |> Seq.tryPick (fun v -> v.FindFirstResponder() |> Option.ofObj)
          |> Option.toObj

如果您必须与 C# 交互,这或多或少是您能做的最好的事情。 Interop 总是有点难看。但是,如果您不再需要 C#,我建议将 IsFirstResponderSubviewsFindFirstResponder 从方法和属性转换为独立函数,并使 FindFirstResponder return option(这是在 F# 中处理可选值的标准方式)。

然后函数变得更漂亮一点:

let findFirstResponder view =
  if isFirstResponder view then Some view
  else view |> subViews |> Seq.tryPick findFirstResponder