F# :: 遍历列表并再次返回
编写一个函数来计算列表中大于或等于平均值的元素数量(为简单起见,使用整数除法)。
仅使用列表结构的单次遍历
!
我已经有一个解决方案,但它涉及ref
变量从闭包 foo'
更改。
我对如何功能在满足[]
时传递值的方式感兴趣?
我使用 ref
的幼稚解决方案:
let foo ls =
let avg = ref 0
let rec foo' xs sumAcc lenAcc =
match xs with
| x'::xs' ->
let s = foo' xs' (x' + sumAcc) (1 + lenAcc)
if x' < !avg then s else s + 1
| [] ->
avg := (sumAcc / lenAcc) //? how to change THIS to functional code ?
0
foo' ls 0 0
编辑(3):
我对性能感兴趣...... 在列表[1..11000]
上,
`(my solution with REF) 5501: elapsed <00.0108708>`
`(nlucaroni) 5501: elapsed <00.0041484>`
`(kvb) 5501: elapsed <00.0029200>` <-- continuation is fastest
`(two pass solution) 5501: elapsed <00.0038364>`
自1.和3.解决方案是非尾递归的,
// simple two-pass solution
let foo2pass (xs : System.Numerics.BigInteger list) =
let len = System.Numerics.BigInteger.Parse(xs.Length.ToString())
let avg = List.sum xs / len
(List.filter (fun x -> x >= avg) xs).Length
两个pass 和 kvb 的版本适用于大列表,即:list [1I .. 10 000 000I]
:
(two-pass solution) 5000001: elapsed <00:00:12.3200438> <-- 12 first time
(two-pass solution) 5000001: elapsed <00:00:06.7956307> <-- 6
(two-pass solution) 5000001: elapsed <00:00:09.1390587> <-- 9? WHY IS THAT
(two-pass solution) 5000001: elapsed <00:00:06.8345791> <-- 6
(two-pass solution) 5000001: elapsed <00:00:09.1071856> <-- 9? WHY IS THAT
每个解 5 次
(kvb tail-recursive) 5000001I: elapsed <00:00:21.1825866> <-- 21 first time
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8113939> <-- stable
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8335997>
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8418234>
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8331327>
,对于 list [1I .. 1 000 000I]
,kvb 的解更快
(two-pass solution) 500001I: elapsed <00:00:01.8975782>
(kvb tail-recursive) 500001: elapsed <00:00:00.6004453>
Write a function that counts the number of elements in the list that are larger than or equal to the average (using integer division for simplicity).
Using just a single traversal
of the list structure!
I already have a solution to this, BUT it involves ref
variable changed from closure foo'
.
I'm interested in a way how to functionally pass value when []
is met?
My naïve solution using ref
:
let foo ls =
let avg = ref 0
let rec foo' xs sumAcc lenAcc =
match xs with
| x'::xs' ->
let s = foo' xs' (x' + sumAcc) (1 + lenAcc)
if x' < !avg then s else s + 1
| [] ->
avg := (sumAcc / lenAcc) //? how to change THIS to functional code ?
0
foo' ls 0 0
EDIT(3):
I was interested in performance...
on list [1..11000]
`(my solution with REF) 5501: elapsed <00.0108708>`
`(nlucaroni) 5501: elapsed <00.0041484>`
`(kvb) 5501: elapsed <00.0029200>` <-- continuation is fastest
`(two pass solution) 5501: elapsed <00.0038364>`
since 1. and 3. solutions are non-tail-recursive,
// simple two-pass solution
let foo2pass (xs : System.Numerics.BigInteger list) =
let len = System.Numerics.BigInteger.Parse(xs.Length.ToString())
let avg = List.sum xs / len
(List.filter (fun x -> x >= avg) xs).Length
two pass and kvb's version works on big lists, ie: list [1I .. 10 000 000I]
:
(two-pass solution) 5000001: elapsed <00:00:12.3200438> <-- 12 first time
(two-pass solution) 5000001: elapsed <00:00:06.7956307> <-- 6
(two-pass solution) 5000001: elapsed <00:00:09.1390587> <-- 9? WHY IS THAT
(two-pass solution) 5000001: elapsed <00:00:06.8345791> <-- 6
(two-pass solution) 5000001: elapsed <00:00:09.1071856> <-- 9? WHY IS THAT
5 times for each solution
(kvb tail-recursive) 5000001I: elapsed <00:00:21.1825866> <-- 21 first time
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8113939> <-- stable
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8335997>
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8418234>
(kvb tail-recursive) 5000001I: elapsed <00:00:14.8331327>
and for list [1I .. 1 000 000I]
, kvb's solution is faster
(two-pass solution) 500001I: elapsed <00:00:01.8975782>
(kvb tail-recursive) 500001: elapsed <00:00:00.6004453>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您只需将平均值与返回值一起传递到堆栈即可:
You just need to pass the average up the stack with the return value:
这是另一个选项:
为了帮助澄清这里发生的情况,我将描述辅助函数的参数:
getCt
函数以确定有多少项目大于它。getCt
函数应调用之前的getCt
函数来查看此之前有多少项大于平均值,然后如果该项也大于平均值,则增加该总数.还可以创建仅使用尾部调用的修改版本,因此即使在任意大小的列表上也不会导致堆栈溢出。为此,我们的 getCt 函数现在需要一个表示到目前为止计数的累加器参数:
Here's another option:
To help clarify what's going on here, I'll describe the parameters for the helper function:
getCt
function to determine how many items were greater than it.getCt
function should call the previousgetCt
function to see how many items prior to this one are greater than the average, and then increment that total if this item was also greater.It's also possible to create a modified version that uses only tail calls, so it won't cause a stack overflow even on lists of arbitrary size. To do this, our
getCt
function now needs an accumulator parameter representing the count so far:Haskell 的懒惰评价确实在“喜结连理”中大放异彩:
Haskell's lazy evaluation really shines in "tying the knot":