Как я могу прикрепить seq к другому по атрибуту? - PullRequest
3 голосов
/ 19 июля 2011

У меня есть последовательность seqs в FSharp . Я хочу присоединить seq к предыдущему, если для него предикат вернется к true.

Пример:

let items = seq [seq[2;3;4];seq[1;5;6;7;1;9];seq[2;3;5;7]]

Я хочу присоединить seq к предыдущему, если seq начинается с 1, поэтому в этом случае должен быть результат:

seq [seq[2;3;4;1;5;6;7;1;9];seq[2;3;5;7]]

Есть ли хороший функциональный способ сделать это?

Я только начал переводить свои длинные вычислительные процессы из C # в F # и очень впечатлен улучшением производительности, которого я смог достичь даже после очень нескольких часов работы и моими знаниями FSfarp для начинающих.

Я купил книгу у Amazon под названием «Начинающий F #». Это действительно здорово, но сейчас я в основном должен работать с секвенциями, списками, картами, коллекциями, и эта тема не объясняется так подробно, как мне нужно. Будет ли кто-нибудь так любезно посоветовать мне хороший ресурс по этим темам?

Спасибо заранее!

Ответы [ 4 ]

3 голосов
/ 19 июля 2011
let joinBy f input =
  let i = ref 0
  input 
  |> Seq.groupBy (fun x ->
    if not (f x) then incr i
    !i)
  |> Seq.map (snd >> Seq.concat)

joinBy (Seq.head >> ((=) 1)) items
2 голосов
/ 19 июля 2011

Как видно из других решений, эта проблема почти обратна вашему последнему вопросу .Поэтому для правильности приведу здесь модифицированную версию моего ответа :

let concatWithPreviousWhen f s = seq {
    let buffer = ResizeArray()

    let flush() = seq { 
        if buffer.Count > 0 then 
            yield Seq.readonly (buffer.ToArray())
            buffer.Clear() }

    for subseq in s do
        if f subseq |> not then yield! flush()
        buffer.AddRange(subseq)

    yield! flush() }

И вы используете это так:

2 голосов
/ 19 июля 2011

Как и в вашем последнем вопросе, нет библиотечной функции, которая делает именно это.Самое простое решение - написать это обязательно, используя IEnumerator.Однако вы можете написать более полезную функцию (которая затем может быть использована и для других целей).

module Seq =
  /// Iterates over elements of the input sequence and groups adjacent elements.
  /// A new group is started when the specified predicate holds about the element
  /// of the sequence (and at the beginning of the iteration).
  /// For example: 
  ///    Seq.groupWhen isOdd [3;3;2;4;1;2] = seq [[3]; [3; 2; 4]; [1; 2]]
  let groupWhen f (input:seq<_>) = seq {
    use en = input.GetEnumerator()
    let running = ref true

    // Generate a group starting with the current element. Stops generating
    // when it founds element such that 'f en.Current' is 'true'
    let rec group() = 
      [ yield en.Current
        if en.MoveNext() then
          if not (f en.Current) then yield! group() 
        else running := false ]

    if en.MoveNext() then
      // While there are still elements, start a new group
      while running.Value do
        yield group() }

Чтобы решить исходную проблему, вы можете проверить, является ли первый элемент последовательности числом, отличным от 1. Вы получите последовательность групп, где группа - это последовательность последовательностей - затемВы можете просто объединить группы:

items 
  |> Seq.groupWhen (fun s -> Seq.head s <> 1)
  |> Seq.map Seq.concat

РЕДАКТИРОВАТЬ: Я также разместил функцию в виде фрагмента (с хорошим форматированием F #) здесь: http://fssnip.net/6A

1 голос
/ 20 июля 2011

Выглядит как складка для меня, как показано ниже. Пытались быть максимально функциональными без значений ref.

let joinBy f (s:'a seq seq) = 
    let (a:'a seq), (b:'a seq seq) = 
        s |> Seq.fold (fun (a,r) se -> 
                         if f se then (se |> Seq.append a,r) 
                         else (se, seq {yield! r; yield a} ) ) 
             (Seq.empty, Seq.empty)
    seq {yield! b; yield a} |> Seq.filter (Seq.isEmpty >> not)


seq [seq[2;3;4];seq[1;5;6;7;1;9];seq[2;3;5;7]]
|> joinBy (Seq.head >> ((=) 1))
|> printfn "%A"
...