F # - алдыңғы, осы және келесі элементтерді циркулярлық Секке қалай топтастыру керек

F # -де соңғы схеманы жақсы түрлендіру әдісі seq [0; 1; 2; 3; 4] сияқты қатарлардың қатарына seq [4,0,1; 0,1,2; 1,2,3; 2,3,4; 3,4,0] ?

Addition: My Seq represents circular data. In this case the vertices of a closed polyline. I need the neighboring elements to compute the angle of each corner.

4

7 жауаптар

Мұнда тек жүйелі пайдаланатын қарапайым шешім бар. Егер кіріс және шығыс әрдайым тізбе болса, тізімді пайдаланатын және кірісті тек бір рет айналып өтетін сәл астам күрделі, бірақ жылдам шешім бар екенін ескеріңіз.

// Example usage: neighbors [0..4]
let neighbors input =
    let inputLength = Seq.length input
    input
   //The sequence needs to be looped three times;
   //the first and last time handle the previous value for the first
   //element in the input sequence and the next value for the last
   //element in the input sequence, respectively.
    |> Seq.replicate 3
   //Start at the previous element of the first element in the input sequence.
    |> Seq.skip (inputLength - 1)
   //Take the same number of elements as the tuple.
    |> Seq.windowed 3
   //Only keep the same number of elements as the original sequence.
    |> Seq.take inputLength
   //Convert the arrays to tuples
    |> Seq.map (fun values ->
        values.[0], values.[1], values.[2])
   //Return the result as a list of tuples.
    |> Seq.toList
8
қосылды
егер Seq.length деп жасасаңыз, біріншіден, Seq функциясын пайдалану керек пе? ақ Seq.replicate кеңейтім бар ма?
қосылды автор jk., көзі
@bytebuster Ия, бұл дұрыс; бірақ үлгі енгізуді ескере отырып, маңызды емес. Егер қайталанатын мәндер болуы мүмкін болса, онда аталған тізімге негізделген шешімді жүзеге асыру жақсы болар еді.
қосылды автор Jack P., көзі
@bytebuster Таңертең қайтадан ойланып көргеннен кейін, Seq.distinct қажет емес мәселені шешудің жақсы әдісі туралы ойланамын және қайталанатын мәндермен кез келген мәселелерге араласпайды.
қосылды автор Jack P., көзі
@jk Мұнда Seq қолданатын жалғыз себеп - бұл функцияны жалпы (бұл тізбектермен, тізімдермен, массивтермен және т.б. жұмыс істей алатын) сақтау. Жауапта айтқанымдай, егер кіріс әрдайым list немесе array секілді нақты жиынтығы болатын болса, осы функцияны іске асырудың жылдам жолы бар . Seq.replicate - бұл ExtCore ішінде берілген функция.
қосылды автор Jack P., көзі
let windowedEx n (s: seq<_>) =
  let r = ResizeArray(s)
  if r.Count > 1 then
    let last = r.[r.Count-1]
    r.Add(r.[0])
    r.Insert(0, last)
  Seq.windowed n r
3
қосылды

Мұнда жақсы жауаптар бар, тағы біреуі. Мен үшін ең қарапайым көрінеді, O (n) күрделілігі бар, сондай-ақ кейбір қателерді тексеруді сақтайды:

// Returns the last element of a sequence.
// Fails on empty sequence
let last xs =
    let len = Seq.length xs - 1
    if len < 0 then failwith "Sequence must be non-empty"
    xs
    |> Seq.skip len
    |> Seq.head

// Converts an array into a tuple
let toTuple = function
    | [|a; b; c|] -> (a, b, c)
    | _ -> failwith "Must be an array with exactly 3 elements"

let windowedBy3 xs =
    seq {
        yield last xs;
        yield! xs;
        yield Seq.head xs
    }
    |> Seq.windowed 3
    |> Seq.map toTuple

// Usage
Seq.init 5 id
|> windowedBy3
|> Seq.iter (printf "%A; ")
3
қосылды

Бұл дұрыс жауап береді, бірақ бірінші элементтің элементі соңғы болып келеді, бірақ бұл мәселе емес, сіз үш нүктенің әрбір жиынтығы үшін бұрышты таба аласыз.

let cycle s =
    Seq.append s (Seq.take 2 s)//append the first two elements to the and
    |> Seq.windowed 3          //create windows of 3
    |> Seq.map (fun a -> (a.[0], a.[1], a.[2]))//create tuples


// test
[0;1;2;3;4] |> cycle

// returns:
>
  val it : seq =
  seq [(0, 1, 2); (1, 2, 3); (2, 3, 4); (3, 4, 0); ...]
3
қосылды

Егер сіз жалқауды қажет етпесеңіз, аралық жиынын пайдалану тиімдірек болуы мүмкін, мысалы:

// get items (i-1, i, i+1) from arr; wrap around at the boundaries
let adj3 i (arr: 'a[]) =
   //modulo operator that works correctly
    let inline (%%) x y = ((x % y) + y) % y
    let len = arr.Length
    arr.[(i - 1) %% len], arr.[i], arr.[(i + 1) %% len]

let windowed3 s = seq { 
    let sarr = s |> Seq.toArray    
    for i = 0 to sarr.Length do 
        yield adj3 i sarr }

Уақыттың күрделілігі O ( n ).

3
қосылды
Жоқ, мен де ойлаймын. Кезектілік соңғы элементін алу кемінде бір рет (кез келген Seq.toArray ), сонымен қатар Seq.skip len , ResizeArray <_> (лар) және т.б.).
қосылды автор Frank, көзі
Модуль операторын ұнатамын. Алғашқы басынан бастап оның соңғы элементін білу керек болса, seq әлі де ленив болуы мүмкін бе?
қосылды автор Goswin, көзі

Мен мұны осылай жасадым:

let neighbors xs =
  match Array.ofSeq xs with
  | [||] -> [||]
  | [|x|] -> [|x, x, x|]
  | xs ->
      let n = xs.Length
      [|yield xs.[n-1], xs.[0], xs.[1]
        for i=1 to n-2 do
          yield xs.[i-1], xs.[i], xs.[i+1]
        yield xs.[n-2], xs.[n-1], xs.[0]|]

Салыстыру, әдетте, модульдік бүтін арифметикаға қарағанда жылдамырақ. Оны тезірек жасау үшін, жиынды алдын-ала бөліп, элементтерді толтырыңыз, орнына өрнектерді пайдаланыңыз.

2
қосылды

Seq.circularWindowed үшін жалпы шешім қалай көрінеді? n терезесінің өлшемін ескере отырып, алдымен n - 1 элементтерін тұтыну керек, ал қалғандары үшін жалқауды сақтайды. Дереккөзде n - 1 элементтерінен артық болмаған жағдайда, ол бос тізбекті шығарады.

Мәселен, бұл кэш үшін ResizeArray және дәйектілік өрнегі.

module Seq =
    let circularWindowed n (xs : seq<_>) =
        let en = xs.GetEnumerator()
        let ra = ResizeArray()
        while ra.Count < n - 1 && en.MoveNext() do
            ra.Add en.Current
        seq{
            if en.MoveNext() then 
                yield! ra
                yield en.Current
                while en.MoveNext() do
                    yield en.Current
                yield! ra }
        |> Seq.windowed n

seq [0; 1; 2; 3; 4]
|> Seq.circularWindowed 3
|> Seq.toList
// val it : int [] list =
//   [[|0; 1; 2|]; [|1; 2; 3|]; [|2; 3; 4|]; [|3; 4; 0|]; [|4; 0; 1|]]
1
қосылды