如何从在 F# 中迭代 for 循环的函数返回值
我正在尝试循环数组并返回一个值,如下所示。但这在 if 语句之后的行上给了我一个错误。它说“这个表达式应该有类型unit,但有类型int”
let findMostSignificantBitPosition (inputBits:System.Collections.BitArray) =
for i = inputBits.Length - 1 to 0 do
if inputBits.[i] then
i
done
我该怎么做?我正在用递归循环重新编码,因为这似乎是在函数语言中进行此类循环的更被接受的方式,但我仍然想知道我上面做错了什么。
I am trying loop over an array and return a value as shown below. But this gives me an error on the line after the if statement. It says "This expression was expected to have type unit but has type int"
let findMostSignificantBitPosition (inputBits:System.Collections.BitArray) =
for i = inputBits.Length - 1 to 0 do
if inputBits.[i] then
i
done
How would I do this? I am in the middle of recoding this with a recursive loop, as it seems to be the more accepted way of doing such loops in functional languages, but I still want to know what I was doing wrong above.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
for
循环不应该返回值,它们只执行固定次数的操作,然后返回()
(单位)。如果你想迭代并最终返回一些东西,你可以:在循环外部有一个引用,当你得到最终结果时将其放入其中,然后在循环之后返回引用内容
直接使用递归函数
使用将封装结果的高阶函数为您遍历,让您专注于应用程序逻辑
如果您的数据结构支持,高阶函数就很好。然而,诸如
fold_left
之类的简单遍历函数不支持过早停止迭代。如果您希望支持这一点(显然这在您的用例中会很有趣),则必须使用具有过早退出支持的遍历。对于像您这样的简单函数,简单的递归函数可能是最简单的。在 F# 中,还应该可以以命令式风格编写函数,使用 yield 将其转换为生成器,然后最终强制生成器获取结果。这可以看作是 OCaml 技术的对应部分,即使用异常跳出循环。
编辑:避免“过早停止”问题的一个好解决方案是使用惰性中间数据结构,该结构只会构建到第一个令人满意的结果。这是优雅且良好的脚本风格,但仍然比直接退出支持或简单递归效率低。我想这取决于你的需求;该函数是否要在关键路径中使用?
编辑:以下是一些代码示例。它们是 OCaml,数据结构不同(其中一些使用 Batteries 中的库),但是想法是一样的。
for
loops are not supposed to return values, they only do an operation a fixed number of times then return()
(unit). If you want to iterate and finally return something, you may :have outside the loop a reference where you put the final result when you get it, then after the loop return the reference content
use a recursive function directly
use a higher-order function that will encapsulate the traversal for you, and let you concentrate on the application logic
The higher-function is nice if your data structure supports it. Simple traversal functions such as
fold_left
, however, don't support stopping the iteration prematurely. If you wish to support this (and clearly it would be interesting in your use case), you must use a traversal with premature exit support. For easy functions such as yours, a simple recursive function is probably the simplest.In F# it should also be possible to write your function in imperative style, using
yield
to turn it into a generator, then finally forcing the generator to get the result. This could be seen as a counterpart of the OCaml technique of using an exception to jump out of the loop.Edit: A nice solution to avoid the "premature stop" questions is to use a lazy intermediate data structure, which will only be built up to the first satisfying result. This is elegant and good scripting style, but still less efficient than direct exit support or simple recursion. I guess it depends on your needs; is this function to be used in a critical path?
Edit: following are some code sample. They're OCaml and the data structures are different (some of them use libraries from Batteries), but the ideas are the same.
当可以使用高阶函数时为什么要使用循环?
我会写:
Seq
模块包含许多用于操作集合的函数。它通常是使用命令式循环的一个很好的替代方案。for
循环的主体是一个类型为unit 的表达式。你唯一能做的就是产生副作用(修改可变值、打印......)。在 F# 中,
if then else
类似于? :
来自C语言。then
和else
部分必须具有相同的类型,否则在静态类型语言中没有意义。当else
缺失时,编译器假定它是else ()
。因此,then
必须具有unit
类型。将值放入for
循环中并不意味着返回
,因为 F# 中的所有内容都是值(包括if then
)。Why using a loop when you can use high-order functions?
I would write:
Seq
module contains many functions for manipulating collections. It is often a good alternative to using imperative loops.The body of a
for
loop is an expression of type unit. The only thing you can do from there is doing side-effects (modifying a mutable value, printing...).In F#, a
if then else
is similar to? :
from C languages. Thethen
and theelse
parts must have the same type, otherwise it doesn't make sense in a language with static typing. When theelse
is missing, the compiler assumes it iselse ()
. Thus, thethen
must have typeunit
. Putting a value in afor
loop doesn't meanreturn
, because everything is a value in F# (including aif then
).+1 加油
以下是 F# 中的一些示例。我添加了一个(第二个)来展示
yield
如何在序列表达式中与for
配合使用,正如 Gasche 提到的那样。编辑:返回选项的版本
+1 for gasche
Here are some examples in F#. I added one (the second) to show how
yield
works withfor
within a sequence expression, as gasche mentioned.Edit: versions returning an Option
我建议使用高阶函数(如 Laurent 提到的)或显式编写递归函数(这是替换 F# 中循环的通用方法)。
如果您想看到一些奇特的 F# 解决方案(这可能是使用某些临时惰性数据结构的更好版本),那么您可以看一下我的文章,其中定义了 F# 的命令式计算生成器。这允许您编写如下内容:
存在一些开销(与使用其他临时惰性数据结构一样),但它看起来就像 C# :-)。
编辑我还在 F# Snippets 上发布了示例:http://fssnip.net/40
I would recommend using a higher-order function (as mentioned by Laurent) or writing a recursive function explicitly (which is a general approach to replace loops in F#).
If you want to see some fancy F# solution (which is probably better version of using some temporary lazy data structure), then you can take a look at my article which defines imperative computation builder for F#. This allows you to write something like:
There is some overhead (as with using other temporary lazy data structures), but it looks just like C# :-).
EDIT I also posted the samples on F# Snippets: http://fssnip.net/40
我认为您在如何编写此代码方面遇到问题的原因是您没有处理找不到设置位的失败情况。其他人发布了许多找到该位的方法。以下是处理失败案例的几种方法。
失败案例(按选项)
失败案例(按异常)
失败案例(按 -1)
I think the reason your having issues with how to write this code is that you're not handling the failure case of not finding a set bit. Others have posted many ways of finding the bit. Here are a few ways of handling the failure case.
failure case by Option
failure case by Exception
failure case by -1
选项之一是使用 seq 和 findIndex 方法:
One of the options is to use seq and findIndex method as: