2017-11-19 1 views
1

Ich mag würde so etwas wie imaginäre Array.multipick implementieren (?):Elegante Array.multipick Implementierung

Array.multipick : choosers:('a -> bool) [] -> array:'a [] -> 'a [] 

Intern Wir prüfen jedes Element des Arrays mit allen choosers, die ersten choosertrue zurückzukehren entfernt von choosers Array, und wir fügen das Argument chooser zu dem Ergebnis hinzu. Danach setzen wir die Integration fort, während choosers Array Elemente übrig hat.

Der letzte Teil ist wichtig, denn ohne frühe Ausstiegsanforderung könnte dies nur mit Array.fold gelöst werden.

let rec impl currentIndex currentChoosers results 

Aber es ist zu prozeduralen für meinen Geschmack:

Dieses leicht mit so etwas wie umgesetzt werden könnten. Vielleicht gibt es eine elegantere Lösung?

Antwort

2

Ein Falter mit frühem Ausstieg ist eine gute Idee, aber ein produktionsreifer, der speziell auf Arrays abzielt, müsste ziemlich zwingend geschrieben werden. Der Einfachheit halber nehme ich die allgemeinere Sequenz von this answer.

let multipick (choosers: ('a -> bool) array) (arr: 'a array) : 'a array = 
    let indexed = 
     choosers 
     |> Seq.indexed 
     |> Map.ofSeq 
    ((indexed, []), arr) 
    ||> foldWhile (fun (cs, res) e -> 
     if Map.isEmpty cs then 
      None 
     else 
      match cs |> Seq.tryFind (fun kvp -> kvp.Value e) with 
      | Some kvp -> Some (Map.remove kvp.Key cs, e :: res) 
      | None  -> Some (cs, res)) 
    |> snd 
    |> List.rev 
    |> Array.ofList 

Ich bin ein Map verkeilte von Array-Index mit Spur verbleibenden Funktionen zu halten - dies ermöglicht eine einfache Entfernung von Elementen, aber noch immer ihre Reihenfolge (da Karte Schlüssel-Wert-Paare werden durch Tasten bestellt, wenn Iterieren).

F # Set würde aufgrund von Vergleichsbedingungen nicht mit Funktionen arbeiten. System.Collections.Generic.HashSet würde funktionieren, aber es ist veränderbar, und ich bin mir nicht sicher, ob es die Reihenfolge beibehalten würde.

+0

yeah, ich hatte ähnliche Gedanken im Sinn! auch faul Seq.scan + Seq.takeWhile Kombination ist ziemlich ordentlich. – yuyoyuppe

3

Es ist ziemlich schwierig, eleganten Code mit Arrays wechselnder Größe zu schreiben. Hier ist ein Code, der stattdessen auf Listen funktioniert und keine Werte mutiert.

let rec pick accum elem tried = function 
    | [] -> (accum, List.rev tried) 
    | chooser :: rest -> 
     if chooser elem then (elem :: accum, List.rev_append tried rest) 
     else pick accum elem (chooser :: tried) rest 

let rec multipick_l accum choosers list = 
    match choosers, list with 
    | [], _ 
    | _, [] -> List.rev accum 
    | _, elem :: elems -> 
     let (accum', choosers') = pick accum elem [] choosers in 
     multipick_l accum' choosers' elems 

let multipick choosers array = 
    Array.of_list 
     (multipick_l [] (Array.to_list choosers) (Array.to_list array)) 

Wenn Sie denken, dass Array.fold_left mit Ausnahme des frühen Ausscheidens Bedarf verwendbar ist, können Sie eine Ausnahme früh zu beenden verwenden.

+0

danke für die Antwort! Ich werde versuchen, etwas wie "Set <'a -> bool" zu implementieren, um später die versuchten Wähler zu verfolgen und dann wahrscheinlich Ihre Antwort zu akzeptieren :) – yuyoyuppe

Verwandte Themen