2017-05-12 1 views
2

Ich möchte eine typsichere rekursive Funktion zum Ausflachen von Tupeln erstellen. Allerdings kann ich nicht unter der ersten Rekursion Ebene in Bezug auf die TypsicherheitWie kann ich typesafe rekursive Inline-Funktion für das Reduzieren von Tupeln schreiben

type Flatten = Flatten 
with 
    static member inline ($) (Flatten, (a: 'a, b: 'b)) : 'x list = 
     List.concat [ Flatten.Flat a; Flatten.Flat b] 
    static member inline($) (Flatten, (a: 'a, b: 'b, c: 'c)) : 'x list = 
     List.concat [Flatten.Flat a; Flatten.Flat b; Flatten.Flat c] 
    static member inline Flat(x: obj) : 'x list = 
     match x with 
     | :? Tuple<'a, 'b> as t -> Flatten $ (t.Item1, t.Item2) 
     | :? Tuple<'a, 'b, 'c> as t ->Flatten $ (t.Item1, t.Item2, t.Item3) 
     | _ -> [x] 
let inline flatten x = Flatten $ x 
let a1 = flatten (1, (2, 2, 3), (3,3)) 
//this compiles 
let a2 = flatten (1, (2, 2, 3, 4), (3,3)) 
//       ^but this too 

ich einen anderen Ansatz versucht

type Flatten = Flatten 
with 
    static member inline ($) (Flatten, (a: 'a, b: 'b)) = List.concat [ Flat $ a; Flat $ b] 
    static member inline ($) (Flatten, (a: 'a, b: 'b, c: 'c)) = List.concat [Flat $ a; Flat $ b; Flat $ c] 

and Flat = Flat 
with 
    static member inline ($) (Flat, a: 'a) = [a] 
    static member inline ($) (Flat, x: ('a *'b)) = 
     let (a, b) = x 
     List.concat [ Flatten $ a; Flatten $ b] 
    static member inline($) (Flat, x : ('a * 'b * 'c)) = 
     let (a, b, c) = x 
     List.concat [Flatten $ a; Flatten $ b; Flatten $ c] 

let inline flatten x = Flatten $ x 
let a = flatten (1, 1) 
let a1 = flatten (1, 1, 3) 
let a2 = flatten (1, 1, (3, 3)) 

aber ich kann nicht, dass eine Kontrolle zu geben bekommen.

Hat jemand eine Ahnung?

Eine weitere Anforderung

Der Grund, warum ich all das tue, ist zum Teil, weil ich

let a1 = flatten (1, (2, 2, 3), (3,3)) 

erhalten möchten

val a1 : int list 

denn das ist, wenn ich in einem Feed Tupel von Tupel von Int dann sollte das einzige vernünftige Ergebnis einsein. im Moment bekomme ich eine obj list Int das erste Beispiel ein Kompilierfehler in der zweiten.

Mit freundlichen Grüßen

+1

Sicher können Sie nicht für alle Tupelgrößen. An einem bestimmten Punkt müssen Sie so viele Überladungen wie möglich Tupelgrößen angeben. – Gustavo

+1

Ich würde nur statische Typisierung Garantien verfallen und Reflexion verwenden. – scrwtp

+1

@Gustavo - Da F # nur die Tupelgrößen 1-8 hat und es für größere Tupelgrößen mit verschachtelten Tupeln "vortäuscht", denke ich, dass es tatsächlich möglich ist, dies für alle Tupelgrößen zu schreiben. Ich habe jedoch nie das Bedürfnis verspürt, dies zu tun, da es normalerweise bessere (IMHO) Lösungen für jedes Problem gibt, das dazu führt, dass man Tupel unbekannter Artigkeit glätten will. – rmunn

Antwort

3

Die .Net Tuple Klasse arities from 1 to 8 in seiner Anzahl von Typparametern hat. Ich glaube, dass in F #, wenn Sie ein Tupel von 8 oder mehr Elementen haben, es als ein Tupel von sieben Elementen plus ein verschachteltes Tupel in dem achten Slot behandelt wird, z. (a,b,c,d,e,f,g,h,i,j) ist wirklich (a,b,c,d,e,f,g,(h,i,j)), ein Tupel des Typs System.Tuple<'T1,'T2,'T3,'T4,'T5,'T6,'T7,System.Tuple<'T8,'T9,'T10>>.

jedoch Ihr erster Ansatz behandelt nur arities 2 und 3, aber Sie testen es mit einer arity-4 Tupel, wenn Sie flatten (1, (2, 2, 3, 4), (3,3)) tun. Was ist, wenn Sie Ihre erste Flat Funktion wie folgt umschreiben?

static member inline Flat(x: obj) : 'x list = 
    match x with 
    | :? Tuple<'a> as t -> Flatten $ (t.Item1) 
    | :? Tuple<'a, 'b> as t -> Flatten $ (t.Item1, t.Item2) 
    | :? Tuple<'a, 'b, 'c> as t ->Flatten $ (t.Item1, t.Item2, t.Item3) 
    | :? Tuple<'a, 'b, 'c, 'd> as t -> Flatten $ (t.Item1, t.Item2, t.Item3, t.Item4) 
    | :? Tuple<'a, 'b, 'c, 'd, 'e, 'f> as t -> Flatten $ (t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6) 
    | :? Tuple<'a, 'b, 'c, 'd, 'e, 'f, 'g> as t -> Flatten $ (t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6, t.Item7) 
    | :? Tuple<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h> as t -> Flatten $ (t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6, t.Item7, t.Item8) 
    | _ -> [x] 

Und natürlich müssen Sie static member inline ($) Implementierungen für jede diese arities von 1 bis 8. Gibt entsprechen, das Ihr Problem zu lösen?

P.S. Beachten Sie, dass ich diesen Code nur in das Antwortfenster in Stack Overflow eingegeben habe. Ich habe es noch nicht getestet.

+0

thx für die Antwort - leider ist es nicht wirklich das, was ich wollte. Bitte schauen Sie auf meine Kommentare oben. Upboot sowieso – robkuz

2

Ich würde gerne die Annahme wetten, dass dies nicht in einer typsicheren Weise ohne Laufzeittest durchgeführt werden kann.

module Tuple = 
    open Microsoft.FSharp.Reflection 
    let rec collect<'T> (x : obj) = [| 
     if FSharpType.IsTuple <| x.GetType() then 
      for y in FSharpValue.GetTupleFields x do 
       yield! collect y 
     elif x :? 'T then yield x :?> 'T |] 

Tuple.collect<int> (((100,101,102),"X"),1,2,3,(4,5)) 
// val it : int [] = [|100; 101; 102; 1; 2; 3; 4; 5|] 

Inline Überladungsauflösung nicht funktioniert, weil F # 's-Typ-System nicht ausdrucksstark genug ist zwischen einem Typ 'T*'T haft Element Constraints 'T und einem Tupel zu erkennen; das Tupel wird notwendigerweise als eine atomare Einheit behandelt 'T. Daher würde das Kompilierungsszenario immer in den atomaren Fall und niemals in die Tupel aufgelöst werden.

Verwandte Themen