我正在努力学习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_;
问题得到解决。但我不想打印清单,我想返回它!
是什么原因引起的?为什么我不能退回我的名单?
答案 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