1

リストの要素を数え、ルールに従い、アキュムレータを使用する末尾再帰関数を実行しようとしましたが、実行すると次のようになります。

lstcountr [1..98765432];;

私はこれを得る:

System.OutOfMemoryException: タイプ 'System.OutOfMemoryException' の例外がスローされました。

これは私の関数です(末尾再帰的/効率的だと思いました):

let lstcountr ls =
    let rec loop ls total = 
        match ls with
        | [] -> total
        | hd::tl -> loop tl total+1I
    loop ls 0I

これはもっとうまくできますか?

4

3 に答える 3

8

関数は末尾再帰ではありません。

| hd::tl -> loop tl total+1I

する必要があります

| hd::tl -> loop tl (total+1I)

演算子は関数呼び出しの後に解釈されます。通常、このような状況では結果が同じであるためわかりませんが、末尾再帰ではそうではありません。

また、Tejsが言ったように、作成しているアイテムのリストが多すぎて、OutOfMemoryException. を使ってみましたseq { }か?

于 2012-04-25T16:27:31.573 に答える
5

再帰が多すぎると、StackOverflowExceptionではなく、を取得することになりOutOfMemoryExceptionます。これは、98765432要素のリストを一度に作成しようとしているためです。

再帰に関係なく、引数として渡されるこのリストはメモリ内に作成されますが、私が追加するのは怠惰ではありません。

于 2012-04-25T16:02:07.187 に答える
2

戻り型がポリモーフィックなシーケンスベースのバージョンを作成する2つの方法を次に示します。

module Seq =
  open LanguagePrimitives

  let inline count items = Seq.fold (fun i _ -> i + GenericOne) GenericZero items

  //faster
  let inline count (items: seq<_>) =
    use e = items.GetEnumerator()
    let rec loop n =
      if e.MoveNext() then loop (n + GenericOne)
      else n
    loop GenericZero

これにより、最適なタイプを使用して長さを計算できます。

let n : bigint = Seq.count {1I .. 987298234982374923847I}
let n : float = Seq.count {1I .. 987298234982374923847I}
于 2012-04-25T20:30:41.260 に答える