编写一个函数,计算列表中大于或等于平均值的元素数量(为简单起见,使用整数除法)。
仅使用列表结构的single traversal
!
我已经有了解决方法,但它涉及从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)的:
我对表演很感兴趣......
在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>`
因为 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
两次传递 和 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>
答案 0 :(得分:9)
您只需要使用返回值传递堆栈的平均值:
let foo ls =
let rec foo xs sumAcc lenAcc = match xs with
| x::xs -> let avg,s = foo xs (x + sumAcc) (1 + lenAcc) in
if x < avg then (avg,s) else (avg,s+1)
| [] -> (sumAcc / lenAcc),0
in
let avg,res = foo ls 0 0 in
res
答案 1 :(得分:7)
这是另一种选择:
let foo =
let rec helper sum ct getCt = function
| x::xs ->
helper (sum+x) (ct+1) (fun avg -> getCt(avg) + (if avg <= x then 1 else 0)) xs
| [] -> getCt(sum/ct)
helper 0 0 (fun avg -> 0)
为了帮助澄清这里发生的事情,我将描述辅助函数的参数:
getCt
函数以确定有多少项目大于它。getCt
函数应该调用上一个getCt
函数,以查看此函数之前的项目数大于平均值,然后如果此项目也更大,则增加该项目总数。也可以创建仅使用尾调用的修改版本,因此即使在任意大小的列表上也不会导致堆栈溢出。为此,我们的getCt
函数现在需要一个累加器参数来表示到目前为止的计数:
let foo =
let rec helper sum ct getCt = function
| x::xs ->
helper (sum+x) (ct+1) (fun avg n -> getCt avg (if avg <= x then n+1 else n)) xs
| [] -> getCt (sum/ct) 0
helper 0 0 (fun avg n -> n)
答案 2 :(得分:0)
Haskell的懒惰评价确实在“打结”中熠熠生辉:
avgList t = let (final, sum, count) = go t 0 0 0
avg = sum `div` count
go [] finalA sumA countA = (finalA, sumA, countA)
go (x:xs) finalA sumA countA = go xs (x' + finalA) (sumA + x) (countA + 1)
where x' = if x >= avg then 1 else 0
in final