F#中的返回值 - 不完整的构造

时间:2010-03-09 23:28:26

标签: f# functional-programming

我正在努力学习F#。我是一个完全的初学者,所以这对你们来说可能是一个小问题:)

我有以下功能:

let removeEven l = 
 let n = List.length l;
 let list_ = [];
 let seq_ = seq { for x in 1..n do if x % 2 <> 0 then yield List.nth l (x-1)}
 for x in seq_ do
  let list_ = list_ @ [x];
 list_;

它需要一个列表,并返回一个包含所有数字的新列表,该列表放在原始列表中的奇数索引处,因此removeEven [x1;x2;x3] = [x1;x3]

但是,我收到了我最喜欢的错误消息:Incomplete construct at or before this point in expression...

如果我在该行末尾添加打印件,而不是list_

...
print_any list_;

问题得到解决。但我不想打印清单,我想返回它!

是什么原因引起的?为什么我不能退回我的名单?

5 个答案:

答案 0 :(得分:6)

编辑:如果我正确理解了您的要求,那么这是一个功能完成功能而非命令式的版本,它会删除带有奇数索引的元素。

let removeEven list =
    list
    |> Seq.mapi (fun i x -> (i, x))
    |> Seq.filter (fun (i, x) -> i % 2 = 0)
    |> Seq.map snd
    |> List.ofSeq

> removeEven ['a'; 'b'; 'c'; 'd'];;
val it : char list = ['a'; 'c']

答案 1 :(得分:6)

要首先回答您的问题,编译器会抱怨,因为for循环内部存在问题。在F#中,let用于声明值(这些值是不可变的,以后不能在程序中更改)。它不是C#中的语句 - let只能用作另一个表达式的一部分。例如:

let n = 10
n + n

实际上意味着您希望n符号引用表达式10中的值n + n。您的代码的问题在于您使用let而没有任何表达式(可能是因为您想使用可变变量):

for x in seq_ do 
  let list_ = list_ @ [x]  // This isn't assignment! 
list_

有问题的行是一个不完整的表达式 - 不允许以这种方式使用let,因为它不包含任何表达式(不会从任何代码访问list_值)。您可以使用mutable变量来更正代码:

let mutable list_ = [] // declared as 'mutable'
let seq_ = seq { for x in 1..n do if x % 2 <> 0 then yield List.nth l (x-1)}    
for x in seq_ do    
  list_ <- list_ @ [x] // assignment using '<-'

现在,这应该可行,但它不是真正的功能,因为你正在使用命令式突变。此外,使用@附加元素在函数式语言中是非常低效的。因此,如果您想使代码正常运行,您可能需要使用不同的方法。其他两个答案都显示了一个很好的方法,虽然我更喜欢Joel的例子,因为索引到一个列表(在Chaos的解决方案中)也不是很有用(没有指针算法,所以它也会慢一些)

最经典的功能解决方案可能是使用List.fold函数,它将列表中的所有元素聚合成一个结果,从左到右:

[1;2;3;4;5] 
  |> List.fold (fun (flag, res) el -> 
     if flag then (not flag, el::res) else (not flag, res)) (true, [])
  |> snd |> List.rev

这里,聚合期间使用的状态是一个布尔标志,指定是否包含下一个元素(在每个步骤中,我们通过返回not flag来翻转标志)。第二个元素是到目前为止聚合的列表(我们仅在设置el::res时添加flag元素。在fold返回后,我们使用snd来获取第二个元素元组(聚合列表)并使用List.rev反转它,因为它是以相反的顺序收集的(这比使用res@[el]追加到最后更有效。)

答案 2 :(得分:2)

我认为这就是你要找的东西。

let removeEven list = 
    let maxIndex = (List.length list) - 1;
    seq { for i in 0..2..maxIndex -> list.[i] }
    |> Seq.toList

<强>测试

val removeEven : 'a list -> 'a list

> removeEven [1;2;3;4;5;6];;
val it : int list = [1; 3; 5]
> removeEven [1;2;3;4;5];;
val it : int list = [1; 3; 5]
> removeEven [1;2;3;4];;
val it : int list = [1; 3]
> removeEven [1;2;3];;
val it : int list = [1; 3]
> removeEven [1;2];;
val it : int list = [1]
> removeEven [1];;
val it : int list = [1]

答案 3 :(得分:0)

您可以尝试使用模式匹配方法。我有一段时间没有使用F#,我现在无法测试,但它会是这样的:

let rec curse sofar ls =
    match ls with
    | even :: odd :: tl -> curse (even :: sofar) tl
    | even :: [] -> curse (even :: sofar) []
    | [] -> List.rev sofar

curse [] [ 1; 2; 3; 4; 5 ]

这递归地挑选偶数元素。我认为。我可能会使用Joel Mueller的方法。我不记得是否有基于索引的filter函数,但这可能是理想的使用,或者如果它不存在于库中则可以。

但一般来说,列表并不真正意味着索引类型的东西。这就是数组的用途。如果您考虑哪种算法需要删除偶数元素的列表,可能在此要求之前的步骤中,元素可以在元组中配对,如下所示:

[ (1,2); (3,4) ]

这样就可以让偶数 - “索引”元素变得微不足道了:

thelist |> List.map fst // take first element from each tuple

如果不保证输入列表具有偶数个元素,则有多种选择。

答案 4 :(得分:0)

另一种替代方案(通过我的估算)比Joel的稍慢,但它更短:)

let removeEven list =
    list
    |> Seq.mapi (fun i x -> (i, x))
    |> Seq.choose (fun (i,x) -> if i % 2 = 0 then Some(x) else None)
    |> List.ofSeq