Skip to content

Commit

Permalink
Import of DList from FSharpx.Collections
Browse files Browse the repository at this point in the history
Using a single file instead of both a DList.fs and DList.fsi.
Adding dependency on FsCheck for the tests.
  • Loading branch information
wallymathieu committed Mar 15, 2018
1 parent 9c03a49 commit c38ef52
Show file tree
Hide file tree
Showing 9 changed files with 1,103 additions and 695 deletions.
4 changes: 2 additions & 2 deletions .paket/Paket.Restore.targets
Expand Up @@ -53,10 +53,10 @@
</PropertyGroup>

<!-- If shasum and awk exist get the hashes -->
<Exec Condition=" '$(PaketRestoreCachedHasher)' != '' " Command="$(PaketRestoreCachedHasher)" ConsoleToMSBuild='true'>
<Exec StandardOutputImportance="Low" Condition=" '$(PaketRestoreCachedHasher)' != '' " Command="$(PaketRestoreCachedHasher)" ConsoleToMSBuild='true'>
<Output TaskParameter="ConsoleOutput" PropertyName="PaketRestoreCachedHash" />
</Exec>
<Exec Condition=" '$(PaketRestoreLockFileHasher)' != '' " Command="$(PaketRestoreLockFileHasher)" ConsoleToMSBuild='true'>
<Exec StandardOutputImportance="Low" Condition=" '$(PaketRestoreLockFileHasher)' != '' " Command="$(PaketRestoreLockFileHasher)" ConsoleToMSBuild='true'>
<Output TaskParameter="ConsoleOutput" PropertyName="PaketRestoreLockFileHash" />
</Exec>

Expand Down
1 change: 1 addition & 0 deletions paket.dependencies
Expand Up @@ -11,3 +11,4 @@ github fsharp/FAKE modules/Octokit/Octokit.fsx
nuget MathNet.Numerics.FSharp redirects: force
nuget NUnit ~> 2
nuget NUnit.Runners ~> 2
nuget FsCheck
1,069 changes: 537 additions & 532 deletions paket.lock

Large diffs are not rendered by default.

62 changes: 1 addition & 61 deletions src/FSharpPlus/App.config
Expand Up @@ -8,66 +8,6 @@
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.4.1.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Linq" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Linq.Expressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Linq.Queryable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Net.WebHeaderCollection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Reflection.Emit.ILGeneration" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Reflection.Emit.Lightweight" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Runtime.Numerics" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Threading" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.12.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Threading.Tasks.Parallel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Threading.Thread" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<Paket>True</Paket>
<assemblyIdentity name="System.Threading.ThreadPool" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.0.11.0" />
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="4.4.3.0" />
</dependentAssembly>
</assemblyBinding></runtime></configuration>
285 changes: 250 additions & 35 deletions src/FSharpPlus/DList.fs
@@ -1,45 +1,260 @@
namespace FSharpPlus.Data
open System.Collections.Generic
open FSharpPlus
// DList from FSharpx.Collections
//This implementation adds an additional parameter to allow O(1) retrieval of the list length.

open System.Runtime.InteropServices

/// List-like type supporting O(1) append
type DList<'T> = DList of ('T list -> 'T list)
/// DList is an ordered linear structure implementing the List signature (head, tail, cons),
/// end-insertion (conj), and O(1) append. Ordering is by insertion history.
/// DList is an implementation of [John Hughes' append list](http://dl.acm.org/citation.cfm?id=8475).
type DList<'T>(length : int , data : DListData<'T> ) =
let mutable hashCode = None
member internal this.dc = data

static member ofSeq (s : seq<'T>) =
DList(Seq.length s, (Seq.fold (fun state x ->
match state with
| Nil -> Unit x
| Unit _ -> Join(state, Unit x)
| Join(_,_) as xs -> Join(state, Unit x)) Nil s))

override this.GetHashCode() =
match hashCode with
| None ->
let mutable hash = 1
for x in this do
hash <- 31 * hash + Unchecked.hash x
hashCode <- Some hash
hash
| Some hash -> hash

override this.Equals(other) =
match other with
| :? DList<'T> as y ->
if this.Length <> y.Length then false
else
if this.GetHashCode() <> y.GetHashCode() then false
else Seq.forall2 (Unchecked.equals) this y
| _ -> false

///O(1). Returns the count of elememts.
member this.Length = length

// O(n). FoldBack walks the DList using constant stack space. Implementation is from Norman Ramsey.
// Called a "fold" in the article processes the linear representation from right to left
// and so is more appropriately implemented under the foldBack signature
// See http://stackoverflow.com/questions/5324623/functional-o1-append-and-on-iteration-from-first-element-list-data-structure/5334068#5334068
static member foldBack (f : ('T -> 'State -> 'State)) (l:DList<'T>) (state : 'State) =
let rec walk lefts l xs =
match l with
| Nil -> finish lefts xs
| Unit x -> finish lefts <| f x xs
| Join(x,y) -> walk (x::lefts) y xs
and finish lefts xs =
match lefts with
| [] -> xs
| t::ts -> walk ts t xs
walk [] l.dc state

// making only a small adjustment to Ramsey's algorithm we get a left to right fold
static member fold (f : ('State -> 'T -> 'State)) (state : 'State) (l:DList<'T>) =
let rec walk rights l xs =
match l with
| Nil -> finish rights xs
| Unit x -> finish rights <| f xs x
| Join(x,y) -> walk (y::rights) x xs
and finish rights xs =
match rights with
| [] -> xs
| t::ts -> walk ts t xs
walk [] l.dc state

static member append (left, right) =
match left with
| Nil -> right
| _ -> match right with
| Nil -> left
| _ -> Join(left, right)

static member appendLists ((left : DList<'T>), (right : DList<'T>)) =
DList( (left.Length + right.Length), (DList<'T>.append(left.dc, right.dc)))

static member head data =
match data with
| Unit x' -> x'
| Join(x',y) -> DList<'T>.head x'
| _ -> failwith "DList.head: empty DList"

static member tryHead data =
match data with
| Unit x' -> Some x'
| Join(x',y) -> DList<'T>.tryHead x'
| _ -> None
///O(1). Returns a new DList with the element added to the front.
member this.Cons (hd : 'T) =
match data with
| Nil -> DList (1, (Unit hd))
| _ -> DList ((length + 1), Join(Unit hd, data) )

///O(log n). Returns the first element.
member this.Head = DList<'T>.head data

///O(log n). Returns option first element
member this.TryHead = DList<'T>.tryHead data

///O(1). Returns true if the DList has no elements.
member this.IsEmpty = match data with Nil -> true | _ -> false

///O(1). Returns a new DList with the element added to the end.
member this.Conj (x:'T) = DList( (length + 1), DList<'T>.append(data, Unit x) )

///O(log n). Returns a new DList of the elements trailing the first element.
member this.Tail =
let rec step (xs:DListData<'T>) (acc:DListData<'T>) =
match xs with
| Nil -> acc
| Unit _ -> acc
| Join(x,y) -> step x (DList<'T>.append(y, acc))
if this.IsEmpty then failwith "DList.tail: empty DList"
else DList( (length - 1), (step data Nil ))

///O(log n). Returns option DList of the elements trailing the first element.
member this.TryTail =
let rec step (xs:DListData<'T>) (acc:DListData<'T>) =
match xs with
| Nil -> acc
| Unit _ -> acc
| Join(x,y) -> step x (DList<'T>.append(y, acc))
if this.IsEmpty then None
else Some (DList( (length - 1), (step data Nil )))

///O(log n). Returns the first element and tail.
member this.Uncons = ((DList<'T>.head data), (this.Tail))

///O(log n). Returns option first element and tail.
member this.TryUncons =
match DList<'T>.tryHead data with
| Some(x) -> Some (x, this.Tail)
| None -> None

member this.toSeq() =
//adaptation of right-hand side of Norman Ramsey's "fold"
let rec walk rights l =
seq {match l with
| Nil ->
match rights with
| [] -> ()
| t::ts -> yield! walk ts t
| Unit x ->
yield x
match rights with
| [] -> ()
| t::ts -> yield! walk ts t
| Join(x,y) -> yield! walk (y::rights) x}

(walk [] data).GetEnumerator()

interface IEnumerable<'T> with
member s.GetEnumerator() = s.toSeq()

interface System.Collections.IEnumerable with
override s.GetEnumerator() = (s.toSeq() :> System.Collections.IEnumerator)

and
DListData<'T> =
| Nil
| Unit of 'T
| Join of DListData<'T> * DListData<'T>

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module DList =
let empty = DList id
let toList (DList x) = x []
let toSeq (DList x) = x [] :> seq<_>
let ofList source = source |> List.append |> DList
let ofSeq source = source |> Seq.toList |> List.append |> DList
let append (DList x) (DList y) = DList (x << y)
let singleton x = DList (fun r -> x::r)
let cons x (DList f) = (DList (fun l -> x::(f l)))
let snoc (DList f) x = (DList (fun l -> f (x::l)))
let fold f x = List.fold f x << toList
let map f (DList x) = x [] |> List.map f |> List.append |> DList
let concat x = List.fold append empty x
let join (DList f) = concat (f [])
let ap f x = join <| map (fun y -> map ((|>) y) f) x
let bind m k = concat << List.map k << toList <| m

open DList

//pattern discriminators (active pattern)
let (|Cons|Nil|) (l : DList<'T>) = match l.TryUncons with Some(a,b) -> Cons(a,b) | None -> Nil

///O(1). Returns a new DList of two lists.
let append left right = DList<'T>.appendLists(left, right)

///O(1). Returns a new DList with the element added to the beginning.
let cons hd (l:DList<'T>) =
match l.Length with
| 0 -> DList(1, Unit hd)
| _ -> DList(l.Length + 1, Join(Unit hd, l.dc) )

///O(1). Returns DList of no elements.
[<GeneralizableValue>]
let empty<'T> : DList<'T> = DList(0, Nil )

///O(n). Fold walks the DList using constant stack space. Implementation is from Norman Ramsey.
/// See http://stackoverflow.com/questions/5324623/functional-o1-append-and-on-iteration-from-first-element-list-data-structure/5334068#5334068
let foldBack (f : ('T -> 'State -> 'State)) (l:DList<'T>) (state : 'State) =
DList<'T>.foldBack f l state

let fold (f : ('State -> 'T -> 'State)) (state : 'State) (l:DList<'T>) =
DList<'T>.fold f state l

///O(log n). Returns the first element.
let inline head (l:DList<'T>) = l.Head

///O(log n). Returns option first element.
let inline tryHead (l:DList<'T>) = l.TryHead

///O(1). Returns true if the DList has no elements.
let inline isEmpty (l:DList<'T>) = l.IsEmpty

///O(1). Returns the count of elememts.
let inline length (l:DList<'T>) = l.Length

///O(1). Returns DList of one elements.
let singleton x = DList(1, Unit x )

///O(1). Returns a new DList with the element added to the end.
let inline conj x (l:DList<'T>) = l.Conj x

///O(log n). Returns a new DList of the elements trailing the first element.
let inline tail (l:DList<'T>) = l.Tail

///O(log n). Returns option DList of the elements trailing the first element.
let inline tryTail (l:DList<'T>) = l.TryTail

///O(log n). Returns the first element and tail.
let inline uncons (l:DList<'T>) = l.Uncons

///O(log n). Returns option first element and tail.
let inline tryUncons (l:DList<'T>) = l.TryUncons

///O(n). Returns a DList of the seq.
let ofSeq s = DList<'T>.ofSeq s

///O(n). Returns a list of the DList elements.
let inline toList l = foldBack (List.cons) l []

///O(n). Returns a seq of the DList elements.
let inline toSeq (l:DList<'T>) = l :> seq<'T>

// additions to fit f#+ :
let inline map f (x:DList<_>) = DList.foldBack (cons << f ) x empty
let concat x = DList.fold append empty x
let inline join (f:DList<DList<_>>) = concat f
let inline ap f x = join <| map (fun y -> map ((|>) y) f) x
let inline bind m k = DList.foldBack (append << k) empty m

type DList<'T> with

static member get_Zero = DList id
static member (+) (DList x, DList y) = DList (x << y)
static member get_Zero = DList( 0, Nil)
static member (+) (x:DList<_>, y:DList<_>) = DList.append x y

static member get_Empty = DList id
static member (<|>) (DList x, DList y) = DList (x << y)
static member get_Empty = DList( 0, Nil)
static member (<|>) (x:DList<_>, y:DList<_>) = DList.append x y

static member ToSeq x = toSeq x
static member ToList x = toList x
static member OfSeq x = ofSeq x
static member OfList x = ofList x
static member Fold (x, f, z, [<Optional>]_impl) = fold f x z

static member Return x = DList (fun r -> x::r)
static member Map (x, f) = map f x
static member (<*>) (f, x) = ap f x
static member Join x = join x
static member (>>=) (x, f) = bind x f
static member ToSeq x = DList.toSeq x
static member ToList x = DList.toList x
static member OfSeq x = DList.ofSeq x
static member Fold (x, f, z) = DList.fold f x z

static member Return x = DList (1, x)
static member Map (x, f) = DList.map f x
static member (<*>) (f, x) = DList.ap f x
static member Join x = DList.join x
static member (>>=) (x, f) = DList.bind x f

0 comments on commit c38ef52

Please sign in to comment.