Seiten

Montag, 13. September 2010

F# Finger Tree und RegEx. Teil 1.

Finger Tree ist eine Datenstruktur aus der Welt der funktionalen Programmierung. Die verständliche und ausführliche Erklärung findet man hier 1, 2.

Hier ist ein sehr interessantes Problem beschrieben, das mittels der Finger Tree-Datenstruktur elegant gelöst wird.

Kurz gefasst: Sei einen regulären Ausdruck R gegeben, der auf einen String S der Länge N angewendet wird und angenommen der String S wird durch Einfügen, Ersetzen oder Löschen einzelner Zeichen geändert. Wie schnell kann die geänderte Zeichenfolge mit dem Ausdruck R erneut verglichen werden. Erstmal scheint es, dass die gesamte Zeichenfolge neu überprüft werden soll. Der Artikel jedoch zeigt, dass man nur O (log n) Zeit für die Neuberechnung benötigt.

Zuerst aber F# Finger Tree Version.
Grundlegende Ansätze zur F#-Implementierung habe ich bei diesen Postings - 1 und 2 - abgeschaut. Für weitere Funktionalitäten - split, concat und find Funktionen - ist die Haskell-Version herangezogen worden.
//Types
type IMeasured<'V> =
abstract inline Value : 'V

let inline measured (v : #IMeasured<_>) = v.Value

type IMonoid<'V> =
abstract inline Zero : 'V
abstract inline Plus : 'V -> 'V -> 'V

type Singleton<'T when 'T : (new : unit -> 'T)> private () =
static let instance = new 'T()
static member Instance = instance


type Node<'T,'V when 'T :> IMeasured<'V>> =
| Node2 of 'V * 'T * 'T
| Node3 of 'V * 'T * 'T * 'T
interface IMeasured<'V> with
member x.Value =
match x with
|Node2 (v,_,_) ->v
|Node3 (v,_,_,_) ->v

type Digit<'T,'V,'M when 'M :> IMonoid<'V> and 'M : (new : unit -> 'M) and 'T :> IMeasured<'V>> =
|One of 'T
|Two of 'T * 'T
|Three of 'T * 'T * 'T
|Four of 'T * 'T * 'T * 'T
interface IMeasured<'V> with
member x.Value =
let monoid = Singleton<'M>.Instance
match x with
|One x-> measured x
|Two(a,b)-> monoid.Plus (measured a) (measured b)
|Three(a,b,c)-> monoid.Plus ((measured a, measured b)||>monoid.Plus) (measured c)
|Four(a,b,c,d)-> monoid.Plus ((measured a, measured b)||>monoid.Plus) ((measured c,measured d)||>monoid.Plus)

type FingerTree<'T, 'V, 'M when 'M :> IMonoid<'V> and 'M : (new : unit -> 'M) and 'T :> IMeasured<'V>> =
| Empty
| Single of 'T
| Deep of 'V * Digit<'T,'V,'M> * FingerTree<Node<'T,'V>,'V,'M> * Digit<'T,'V,'M>
interface IMeasured<'V> with
member x.Value =
let monoid = Singleton<'M>.Instance
match x with
| Empty -> monoid.Zero
| Single s -> measured s
| Deep (v,_,_,_)->v

//Tree Construction.
let inline node2<'T,'V,'M when 'M :> IMonoid<'V> and 'M : (new : unit -> 'M) and 'T :> IMeasured<'V>> (a:'T) (b:'T)=
let monoid = Singleton<'M>.Instance
Node2 (monoid.Plus (measured a) (measured b),a,b)

let inline node3<'T,'V,'M when 'M :> IMonoid<'V> and 'M : (new : unit -> 'M) and 'T :> IMeasured<'V>> (a:'T) (b:'T) (c:'T)=
let monoid = Singleton<'M>.Instance
Node3 (monoid.Plus ((measured a, measured b)||>monoid.Plus) (measured c),a,b,c)

let inline consDigit a dig=
match dig with
|One b->Two(a,b)
|Two(b,c)->Three(a,b,c)
|Three(b,c,d)-> Four(a,b,c,d)
|_->raise FTreeException

let rec push_front<'T,'V,'M when 'M :> IMonoid<'V> and 'M : (new : unit -> 'M) and 'T :> IMeasured<'V>> (a:'T) (t:FingerTree<'T,'V,'M> ):FingerTree<'T,'V,'M> =
match t with
|Empty-> Single a
|Single b->deep (One a) Empty (One b)
|Deep (v, left, mid, right)->
let monoid = Singleton<'M>.Instance
match left with
|Four(e,f,g,h) ->
Deep(monoid.Plus (measured a) v,Two(a,e),push_front (node3<'T,'V,'M> f g h) mid,right)
|_->
Deep (monoid.Plus (measured a) v, consDigit a left, mid, right)
Der vollständige Quellcode - fingertree.fsx.
Wie wir sehen können ist die Baumstruktur mit einem Monoid parametrisiert. Dadurch kann eine und dieselbe Baumstruktur für verschiedene Zwecke verwendet werden. Wir können z.B. eine Random Access Datenstruktur definieren.
open FData.FingerTree

//Random Access
type Monoid() =
interface IMonoid<int> with
member this.Zero = 0
member this.Plus a b = a + b

type Element<'T> =
|Element of 'T
interface IMeasured<int> with
member this.Value = 1

type RandomAccess<'T> = FingerTree<Element<'T>, int, Monoid>

//Index-Zugriff.
let nth index tree =
match (find ((<) index) tree) with
|Some value-> value
|None-> failwith "invalid index"

oder Max-Priority Queue
open FData.FingerTree

//Max-Priority Queue
type Monoid() =
interface IMonoid<int> with
member this.Zero = System.Int32.MinValue
member this.Plus a b = max a b

type Element<'V> =
{ Prio : int
Element : 'V }
interface IMeasured<int> with
member this.Value = this.Prio

type Priority<'T> =FingerTree<Element<'T>,int,Monoid>

//Das Element mit der höchsten Priorität finden
let findPrio tree =
match tree with
|Empty->failwith "tree is empty"
|Single b->Some b
|Deep (v, _,_,_)->
find (fun x->x = v) tree

Teil 2.

Keine Kommentare:

Kommentar veröffentlichen