1

私が Python で書くとき、私はいつも F# を使っているかのように代替案を考えようとします:

ここでseqはタプル(key, value1, value2, ...)を単純化して、長さ 2 だけにしています。キーには重複した数字が含まれています。

let xs = Seq.zip [|1;2;2;3;3;3;4;1;1;2;2|] {0..10} // so this is a seq of tuple

[(1, 0),
 (2, 1),
 (2, 2),
 (3, 3),
 (3, 4),
 (3, 5),
 (4, 6),
 (1, 7),
 (1, 8),
 (2, 9),
 (2, 10)]

ここで、 を入力として受け取り、元の seq のサブセットである をseq返す関数を作成したいと思います。seq

キーが変更されたすべてのアイテムをキャプチャし、最初と最後のアイテムがseqまだ存在しない場合はそれらを含める必要があります。

f1(xs) = [(1, 0), (2, 1), (3, 3), (4, 6), (1, 7), (2, 9), (2, 10)]
f1([]) = []

以下は私のpythonコードです。動作しますが、あまり好きではありません。

xs = zip([1,2,2,3,3,3,4,1,1,2,2], range(11))

def f1(xs):
    if not xs:
        return
    last_a = None # I wish I don't have to use None here.
    is_yield = False
    for a, b in xs:
        if a != last_a:
            last_a = a
            is_yield = True
            yield (a, b)
        else:
            is_yield = False
    if not is_yield:
        yield (a, b) # Ugly, use variable outside the loop.

print list(f1(xs))
print list(f1([]))

itertoolsライブラリを使用する別の方法を次に示します。

def f1(xs):
    group = None
    for _, group_iter in itertools.groupby(xs, key = lambda pair: pair[0]):
        group = list(group_iter)
        yield group[0]

    # make sure we yield xs[-1], doesn't work if xs is iterator.
    if group and len(group) > 1: # again, ugly, use variable outside the loop.
        yield group[-1]

F# では、PythonSeq.groupByとは動作が異なります。groupbyこの問題を可能な限り機能的に解決し、参照セルを減らしmutable、 を減らし、あまり手間をかけずに解決するにはどうすればよいか疑問に思っています。

4

5 に答える 5

3

最も簡単な解決策は、シーケンスを配列に変換し、John のアプローチをインデックスで最初と最後の要素を取得することと組み合わせることです。ただし、ミックスに追加する別のソリューションを次に示します。

let f getKey (items: seq<_>) =
  use e = items.GetEnumerator()
  let rec loop doYield prev =
    seq {
      if doYield then yield prev
      if e.MoveNext() then 
        yield! loop (getKey e.Current <> getKey prev) e.Current
      elif not doYield then yield prev
    }
  if e.MoveNext() then loop true e.Current
  else Seq.empty

//Usage: f fst xs
于 2013-10-28T21:33:56.340 に答える
3

機能するはずだが、特に美しくも短くもない再帰的なソリューションは、次のようになります。ただし、パターン マッチングを使用すると、間違いなく少し良くなります。

let whenKeyChanges input = seq {
  /// Recursively iterate over input, when the input is empty, or we found the last
  /// element, we just return it. Otherwise, we check if the key has changed since
  /// the last produced element (and return it if it has), then process the rest 
  let rec loop prevKey input = seq {
    match input with
    | [] -> ()
    | [last] -> yield last
    | (key, value)::tail ->
        if key <> prevKey then yield (key, value)
        yield! loop key tail }

  // Always return the first element if the input is not empty
  match List.ofSeq input with
  | [] -> ()
  | (key, value)::tail -> 
      yield (key, value)
      yield! loop key tail }

より優れた、もう少し宣言的なソリューションが必要な場合は、BlueMountain Capital で取り組んできたデータ フレームと時系列ライブラリを使用できます (まだ正式に発表されていませんが、機能するはずです)。

// Series needs to have unique keys, so we add an index to your original keys
// (so we have series with (0, 1) => 0;  (1, 2) => 1; ...
let xs = series <| Seq.zip (Seq.zip [0..10] [1;2;2;3;3;3;4;1;1;2;2]) [0..10]

// Create chunks such that your part of the key is the same in each chunk
let chunks = xs |> Series.chunkWhile (fun (_, k1) (_, k2) -> k1 = k2)

// For each chunk, return the first element, or the first and the last
// element, if this is the last chunk (as you always want to include the last element)
chunks 
|> Series.map (fun (i, k) chunk -> 
    let f = Series.firstValue chunk
    let l = Series.lastValue chunk
    if (i, k) = Series.lastKey chunks then
      if f <> l then [k, f; k, l] else [k, l]
    else [k, f]) 
// Concatenate the produced values into a single sequence
|> Series.values |> Seq.concat

チャンクは、ここで必要な重要な操作です (ドキュメントを参照してください)。唯一のトリッキーなことは、最後の要素を返すことです-これは複数の異なる方法で処理できます-私が使用したものが最も良いかどうかはわかりません.

于 2013-10-28T20:55:54.287 に答える
1

このようなものがうまくいくと思います

let remove dup = 
    dup
    |> Seq.pairwise
    |> Seq.filter (fun ((a,b),(c,d)) -> a <> c)
    |> Seq.map fst     
于 2013-10-28T20:35:56.537 に答える
0

正しいソリューションでは、最後の要素に関する特殊なケースを満たすために、シーケンスの終わりを認識する必要があります。したがって、処理前に長さがわかるように2つのパスが必要です(たとえば、Tomasのソリューション-最初のパスはリストへのコピーであり、seqとは異なり、反復時に「終了」を公開します)、またはIEnumerableメソッドに依存する必要があるため、次のように知る必要があります最後に達したときに繰り返します(たとえば、ダニエルのソリューション)。

以下は、ジョンのコードの優雅さに触発されていますが、前もって長さを取得することで特殊なケースを処理します (2 パス)。

let remove dup =
    let last = Seq.length dup - 2
    seq{
        yield Seq.head dup
        yield! dup
               |> Seq.pairwise
               |> Seq.mapi (fun i (a,b) -> if fst a <> fst b || i = last then Some(b) else None)
               |> Seq.choose id
    }
于 2013-10-29T05:00:55.723 に答える