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
- Some
或 None
。然后 Seq.tryPick
将 return 它遇到的第一个 Some
,或者如果序列中没有 Some
则 None
。要从可为空的 .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#,我建议将 IsFirstResponder
、Subviews
和 FindFirstResponder
从方法和属性转换为独立函数,并使 FindFirstResponder
return option
(这是在 F# 中处理可选值的标准方式)。
然后函数变得更漂亮一点:
let findFirstResponder view =
if isFirstResponder view then Some view
else view |> subViews |> Seq.tryPick findFirstResponder
我是 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
- Some
或 None
。然后 Seq.tryPick
将 return 它遇到的第一个 Some
,或者如果序列中没有 Some
则 None
。要从可为空的 .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#,我建议将 IsFirstResponder
、Subviews
和 FindFirstResponder
从方法和属性转换为独立函数,并使 FindFirstResponder
return option
(这是在 F# 中处理可选值的标准方式)。
然后函数变得更漂亮一点:
let findFirstResponder view =
if isFirstResponder view then Some view
else view |> subViews |> Seq.tryPick findFirstResponder