我最近开始使用F#并实现了一个非常基本的递归函数,它代表了Eratosthenes的Sieve。我提出了以下工作代码:
static member internal SieveOfEratosthenesRecursive sequence accumulator =
match sequence with
| [] -> accumulator
| head::tail -> let rest = tail |> List.filter(fun number -> number % head <> 0L)
let newAccumulator = head::accumulator
Prime.SieveOfEratosthenesRecursive rest newAccumulator
这个函数实际上没有内存效率,所以我试图消除变量“rest”和“newAccumulator”。我想出了以下代码
static member internal SieveOfEratosthenesRecursive sequence accumulator =
match sequence with
| [] -> accumulator
| head::tail -> tail |> List.filter(fun number -> number % head <> 0L)
|> Prime.SieveOfEratosthenesRecursive (head::accumulator)
据我所知,我读过Prime.SieveOfEratosthenesRecursive的教程将使用过滤后的 tail 作为第一个参数和一个由 head :: accumulator 组成的列表来调用作为第二个。但是,当我尝试使用减少的变量用法运行代码时,程序会在无限循环中陷入陷阱。为什么会发生这种情况?我做错了什么?
答案 0 :(得分:1)
据我所知,我读过Prime.SieveOfEratosthenesRecursive的教程将使用筛选后的
tail
作为第一个参数调用,并将head::accumulator
列为第二个参数。
你有这个倒退。
在第一个版本中,您传递的是rest
,然后是newAccumulator
;在第二个版本中,您有效地传递了newAccumulator
然后rest
。即,你已经改变了论点。
Prime.SieveOfEratosthenesRecursive (head::accumulator)
是一个部分函数应用程序,其中您将(head::accumulator)
作为第一个参数(sequence
)。这个部分函数应用程序产生一个一元函数(期望accumulator
),您将在代码的第一个版本中传递(|>
)所谓的rest
。
更改SieveOfEratosthenesRecursive
的参数顺序是最简单的解决方案,但我会考虑以下惯用语:
static member internal SieveOfEratosthenesRecursive sequence accumulator =
match sequence with
| [] -> accumulator
| head::tail ->
tail
|> List.filter(fun number -> number % head <> 0L)
|> Prime.SieveOfEratosthenesRecursive <| (head::accumulator)
或
static member internal SieveOfEratosthenesRecursive sequence accumulator =
let inline flipzip a b = b, a
match sequence with
| [] -> accumulator
| head::tail ->
tail
|> List.filter(fun number -> number % head <> 0L)
|> flipzip (head::accumulator)
||> Prime.SieveOfEratosthenesRecursive
FWIW,在这里删除rest
和newAccumulator
作为命名变量不会影响你的内存使用量。
答案 1 :(得分:1)
第二个函数中的最后一个调用相当于:
Prime.SieveOfEratosthenesRecursive newAccumulator rest
你切换两个参数的位置。由于newAccumulator
在每次递归调用后变大,因此您永远不会达到空列表的基本情况。
经验法则是最后使用最频繁变化的参数:
let rec sieve acc xs =
match xs with
| [] -> acc
| x::xs' -> xs' |> List.filter (fun y -> y % x <> 0L)
|> sieve (x::acc)
可以使用function
关键字缩短上述功能:
let rec sieve acc = function
| [] -> acc
| x::xs' -> xs' |> List.filter (fun y -> y % x <> 0L)
|> sieve (x::acc)
使用管道(|>
)运算符只会使函数更具可读性,它根本不会影响内存使用。