This is how the List module does it:
This is from the F# library source, which is available to everyone in the distribution \lib\mllib\list.fs
let rec of_fresh_IEnumerator (e : IEnumerator<_>) =
let mutable res = [] in
while e.MoveNext() do
res <- e.Current :: res
done;
List.rev res
let of_IEnumerable (c :> IEnumerable<_>) =
of_fresh_IEnumerator (c.GetEnumerator())
This is from the F# library source, which is available to everyone in the distribution \lib\mllib\list.fs
The use of mutable variables and while loops kills my inner child so here's one for the purely functional geeks.
well as least as pure as we can make it... given an IEnumerator
unfoldr would be a handy function to have in the standard library... essentially the reverse of a fold.
It goes from a value to a list rather than a list to a value.
let rec unfoldr f b =
match f b with
| Some (a, new_b) -> a :: unfoldr f new_b
| None -> []
let enumueratorToList (e:IEnumerator<_>) =
let func (e:IEnumerator<_>) = if e.MoveNext()
then Some (e.Current, e)
else None in
unfoldr func e
well as least as pure as we can make it... given an IEnumerator
unfoldr would be a handy function to have in the standard library... essentially the reverse of a fold.
It goes from a value to a list rather than a list to a value.
val unfoldr : ('seed -> ('a * 'seed) option) -> 'seed -> 'a list
While I agree with trying to save one's inner child, one problem with your recursive implementation is that it isn't tail recursive. I am sure there is some way to do something continuation passing to make it efficient without a while loop, though.
One of things I like about F# (and O'Caml) is that a loop can be used when necessary or convenient and then hidden as far as possible from all of our inner children :-)
One of things I like about F# (and O'Caml) is that a loop can be used when necessary or convenient and then hidden as far as possible from all of our inner children :-)
It is not tail recursive, this is true... and an issue in F#... but not an issue in Haskell which is where i nicked the definition from.
Both the while solution and the unfoldl solution need to have the list reversed... unless we use a doubly linked list.
The unfoldr is nice if we are using lazy lists.
Personally I avoid loops at all costs as I much prefer higher order functions. They are almost always shorter to write and are more declarative.
let rec unfoldl' acc f b =
match f b with
| Some (a, new_b) -> unfoldl' (a::acc) f new_b
| None -> acc
let unfoldl f b = unfoldl' [] f b |> List.rev
Both the while solution and the unfoldl solution need to have the list reversed... unless we use a doubly linked list.
The unfoldr is nice if we are using lazy lists.
Personally I avoid loops at all costs as I much prefer higher order functions. They are almost always shorter to write and are more declarative.
Reversing list is not need if acc is an accumulator function.
I prefer higher order function too.
let comp f g x = f (g x)
let rec unfoldl' acc f b =
let Cons x y = x :: y in
match f b with
| Some(a, new_b) -> unfoldl' ( comp acc (Cons a) ) f new_b
| None -> acc
let unfoldl f b = unfoldl' id f b []
I prefer higher order function too.
Nice work! The functions in the IEnumerable module are fantastically useful, particularly unfold, but also IEnumerable.to_list etc.
While we're on the topic, I'd suggest unfold should be added to the List, Array, LazyList modules (possibly also Set and Map).
Don
While we're on the topic, I'd suggest unfold should be added to the List, Array, LazyList modules (possibly also Set and Map).
Don
Ah I didn't notice unfold was in the IEnumerable module... oh and it already exists for LazyList
So am I correct in understanding in that it hides a lazy list inside the IEnumerable interface (In the IEnumerable module)?
Why no fold for LazyList? There is 'folds' which seems interesting.
Not sure how I haven't noticed it before but the ordering for folds is somewhat inconsistent... and F# has inherited this from O'Caml for portability.
val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a list -> 'b
val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
and the same respectively for arrays
val fold : ('a -> 'b -> 'b) -> Set<'a> -> 'b -> 'b
val fold : ('key -> 'a -> 'c -> 'c) -> Map <'key,'a> -> 'c -> 'c
heh I need to watch out for that...
Folds might be handy for Array2 and Array3 too
So am I correct in understanding in that it hides a lazy list inside the IEnumerable interface (In the IEnumerable module)?
Why no fold for LazyList? There is 'folds' which seems interesting.
Not sure how I haven't noticed it before but the ordering for folds is somewhat inconsistent... and F# has inherited this from O'Caml for portability.
val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a list -> 'b
val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
and the same respectively for arrays
val fold : ('a -> 'b -> 'b) -> Set<'a> -> 'b -> 'b
val fold : ('key -> 'a -> 'c -> 'c) -> Map <'key,'a> -> 'c -> 'c
heh I need to watch out for that...
Folds might be handy for Array2 and Array3 too
Thanks for the feedback - I've posted your comments to the internal F# team aliases to make sure we incorporate it.
Fold/folds on a lazy list in a strict language like F# is an interesting topic: we were discussing it at work during the week. fold_left on a lazy list becomes properly powerful (e.g. enough to define LazyList.map) if the accumulator is guaranteed to be a lazy computation (otherwise the entire lazy list must necessarily be evaluated as part of the fold, giving no chance to take the laziness of the list into the laziness of the accumulation). A sample signature of a lazy-preserving fold is:
rather than the all-too-strict:
Try defining LasyList.map with the second and you'll see what I mean (it can't be done), though using the first to do so is no piece of cake.
(forgive me for the lack of full examples and explanation, or if I've made mistakes! Late at night over here :-) Navigating the lazy divide in a strict language is, however, a fascinating topic!)
Don
Fold/folds on a lazy list in a strict language like F# is an interesting topic: we were discussing it at work during the week. fold_left on a lazy list becomes properly powerful (e.g. enough to define LazyList.map) if the accumulator is guaranteed to be a lazy computation (otherwise the entire lazy list must necessarily be evaluated as part of the fold, giving no chance to take the laziness of the list into the laziness of the accumulation). A sample signature of a lazy-preserving fold is:
val lazy_fold_left : (Lazy<'b> -> 'a -> 'b) -> 'b -> LazyList<'a> -> Lazy<'b>
rather than the all-too-strict:
val fold_left : ('b -> 'a -> 'b) -> 'b -> LazyList<'a> -> 'b
Try defining LasyList.map with the second and you'll see what I mean (it can't be done), though using the first to do so is no piece of cake.
(forgive me for the lack of full examples and explanation, or if I've made mistakes! Late at night over here :-) Navigating the lazy divide in a strict language is, however, a fascinating topic!)
Don
I'm confused, I thought fold_right was ideal in a lazy language/lazy lists and fold_left is ideal in a strict language. And you want the computation of the accumulator to be *strict*.
foldl :: (a -> b -> a) -> a -> [ b ] -> a
foldl f a [] = a
foldl f a x:xs = foldl f (f a x) xs
(excuse my Haskell ;))
If the computation of the accumulator is lazy then you are just going to build up a huge series of deferred computations...
(f (f (f (f init x1) x2) x3) x4)
until you get to the end of the lazy list. So you really want the computation to be strict to avoid this space consumption.
foldr on the other hand makes good use of the lazyness
foldr :: (a -> b -> b) -> b -> [ a ] -> b
foldr f a [] = a
foldr f a x:xs = f x (foldr f a xs)
If you want to define Lazy.map with the mapping function 'g' then surely the function 'f' passed to the fold would be 'lazy cons' composed with 'g'... and you would want to pass that to foldr.
1) the ordering is kept
2) the first mapped value is available and the rest is delayed.
Neither of these two properties would hold for foldl with a lazy list and a lazy operator.
With lazy lists... foldr is useful wither operators like lazy cons, lazy list concatenation and lazy and/or
I believe I'm correct in saying that the time complexity of concatenating a list (lazy or not) of lazy lists using lazy concatenation and foldr will be linear, rather than the intuative quadratic.
I'm pretty sure you switched left with right... maybe that happened when you flipped the candle over to burn it from the other end ;).
However I will post my own caveats... this understanding is all coming from a Haskell background where everything is lazy... here you have to contend with intermixed lazyness and strictiness... as if lazyness on it's own didn't get crazy enough. I'm fairly certainly that my comments are correct here... and fortunately I am currently 5 hours behind GMT.
foldl :: (a -> b -> a) -> a -> [ b ] -> a
foldl f a [] = a
foldl f a x:xs = foldl f (f a x) xs
(excuse my Haskell ;))
If the computation of the accumulator is lazy then you are just going to build up a huge series of deferred computations...
(f (f (f (f init x1) x2) x3) x4)
until you get to the end of the lazy list. So you really want the computation to be strict to avoid this space consumption.
foldr on the other hand makes good use of the lazyness
foldr :: (a -> b -> b) -> b -> [ a ] -> b
foldr f a [] = a
foldr f a x:xs = f x (foldr f a xs)
If you want to define Lazy.map with the mapping function 'g' then surely the function 'f' passed to the fold would be 'lazy cons' composed with 'g'... and you would want to pass that to foldr.
1) the ordering is kept
2) the first mapped value is available and the rest is delayed.
Neither of these two properties would hold for foldl with a lazy list and a lazy operator.
With lazy lists... foldr is useful wither operators like lazy cons, lazy list concatenation and lazy and/or
I believe I'm correct in saying that the time complexity of concatenating a list (lazy or not) of lazy lists using lazy concatenation and foldr will be linear, rather than the intuative quadratic.
I'm pretty sure you switched left with right... maybe that happened when you flipped the candle over to burn it from the other end ;).
However I will post my own caveats... this understanding is all coming from a Haskell background where everything is lazy... here you have to contend with intermixed lazyness and strictiness... as if lazyness on it's own didn't get crazy enough. I'm fairly certainly that my comments are correct here... and fortunately I am currently 5 hours behind GMT.
Luckily I went to bed before I made any more mistakes! Yes, switched left and right (no, I mean right and left ;) )
rather than the all-too-strict:
Don
val lazy_fold_right : ('a -> Lazy<'b> -> 'b) -> LazyList<'a> -> 'b -> Lazy<'b>
rather than the all-too-strict:
val fold_right : ('a -> 'b -> 'b) -> LazyList<'a> -> 'b -> 'b
open Lazy open Stream (* nb. legacy name - still needed to access names 'Cons' and 'Nil' *) open LazyList let rec lazy_fold_right (f : 'a -> Lazy<'b> -> 'b) ll (s : 'b) : Lazy<'b> = lazy match LazyList.get ll with | Cons(h,t) -> f h (lazy_fold_right f t s) | Nil -> s let consl x xs = LazyList.consf x (fun () -> Lazy.force xs) let swallow ll = LazyList.delayed (fun () -> Lazy.force ll) let map f ll = lazy_fold_right (fun x xs -> consl (f x) xs) ll (empty()) |> swallow;;
Don
Topic tags
- f# × 3656
- compiler × 263
- functional × 199
- c# × 119
- websharper × 112
- classes × 96
- web × 94
- book × 84
- .net × 82
- async × 72
- parallel × 43
- server × 43
- parsing × 41
- testing × 41
- asynchronous × 30
- monad × 28
- ocaml × 26
- tutorial × 26
- haskell × 25
- workflows × 22
- html × 21
- linq × 21
- introduction × 19
- silverlight × 19
- wpf × 19
- fpish × 18
- collections × 14
- pipeline × 14
- templates × 12
- monads × 11
- opinion × 10
- reactive × 10
- plugin × 9
- scheme × 9
- sitelets × 9
- solid × 9
- basics × 8
- concurrent × 8
- deployment × 8
- how-to × 8
- python × 8
- complexity × 7
- javascript × 6
- jquery × 6
- lisp × 6
- real-world × 6
- workshop × 6
- xaml × 6
- conference × 5
- dsl × 5
- java × 5
- metaprogramming × 5
- ml × 5
- scala × 5
- visual studio × 5
- formlets × 4
- fsi × 4
- lift × 4
- sql × 4
- teaching × 4
- alt.net × 3
- aml × 3
- enhancement × 3
- reflection × 3
- blog × 2
- compilation × 2
- computation expressions × 2
- corporate × 2
- courses × 2
- cufp × 2
- enterprise × 2
- entity framework × 2
- erlang × 2
- events × 2
- f# interactive × 2
- fsc × 2
- google maps × 2
- html5 × 2
- http × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- keynote × 2
- list × 2
- mvc × 2
- numeric × 2
- obfuscation × 2
- oop × 2
- packaging × 2
- pattern matching × 2
- pipelines × 2
- rx × 2
- script × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- type × 2
- type provider × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- addin × 1
- agents × 1
- agile × 1
- android × 1
- anonymous object × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net mvc × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- b-tree × 1
- bistro × 1
- bug × 1
- camtasia studio × 1
- canvas × 1
- class × 1
- client × 1
- clojure × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- combinator × 1
- confirm × 1
- constructor × 1
- continuation-passing style × 1
- coords × 1
- coursera × 1
- csla × 1
- css × 1
- data × 1
- database × 1
- declarative × 1
- delete × 1
- dhtmlx × 1
- discriminated union × 1
- distance × 1
- docs × 1
- documentation × 1
- dol × 1
- domain × 1
- du × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- error × 1
- etw × 1
- euclidean × 1
- event × 1
- example × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- fantomas × 1
- fear × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- function × 1
- functional style × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- google × 1
- group × 1
- hash × 1
- history × 1
- hosting × 1
- httpcontext × 1
- https × 1
- hubfs × 1
- ie 8 × 1
- if-doc × 1
- inheritance × 1
- installer × 1
- interpreter × 1
- io × 1
- ios × 1
- ipad × 1
- kendo × 1
- learning × 1
- licensing × 1
- macro × 1
- macros × 1
- maps × 1
- markup × 1
- marshal × 1
- math × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- multidimensional × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- nested × 1
- nested loops × 1
- node × 1
- object relation mapper × 1
- object-oriented × 1
- offline × 1
- option × 1
- orm × 1
- osx × 1
- owin × 1
- paper × 1
- parameter × 1
- performance × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- programming × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- ptvs × 1
- quant × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- real-time × 1
- reference × 1
- restful × 1
- round table × 1
- runtime × 1
- scriptcs × 1
- scripting × 1
- service × 1
- session-state × 1
- sitelet × 1
- stickynotes × 1
- stress × 1
- strong name × 1
- structures × 1
- tdd × 1
- template × 1
- tracing × 1
- tsunamiide × 1
- type inference × 1
- type providers × 1
- upload × 1
- vb × 1
- vb.net × 1
- vector × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio shell × 1
- visualstudio × 1
- web api × 1
- webapi × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- xml × 1
|
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us |
Built with WebSharper |
Basically I would like to take all the elements from an enumerable collection and convert it to a list. Here is a sample of code that I have written, though I think there must be a cleaner way to do this.
let enumerator_to_list(e:IEnumerator) = e.Reset(); let l = new List<_>() in while e.MoveNext() do l.Add(e.Current) done; l.ToArray() |> List.of_arrayHere is another example of what I am trying to do:
let node_children node = match node with | null -> [] | cn -> let y = new List<XmlNode>() in for i = 0 to (cn:>XmlNode).ChildNodes.Count - 1 do y.Add(cn.ChildNodes.Item(i)) done; y.ToArray() |> List.of_array(Here the ChildNodes Property has an enumerator)Is there an easier way to do this?
Thanks
Chris