I tried this just now and came up with the following. I'm sure there is a more refined technique I can use, but this was a great learning exercise for me! This isn't perfect due to the mutable field in the node, in particular in the article you linked they use a lazy evaluation of the node, but there might be a way to eliminate it. Maybe someone smarter than me can figure it out :)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
type dbl_list = | None | Node of dbl_node and dbl_node = { mutable Prev:dbl_list; Element:int; Next:dbl_list; } let dbl_cons x list = match list with | None -> Node({ Prev=None; Element=x; Next=None }) | Node(node) -> let new_node = { Prev=None; Element=x; Next=Node(node) } node.Prev <- Node(new_node) Node(new_node) let rec print_dbl_list head = match head with | None -> () | Node(x) -> let prev = match x.Prev with | None -> "--" | Node(y) -> y.Element.ToString() let cur = x.Element.ToString() let next = match x.Next with | None -> "--" | Node(y) -> y.Element.ToString() printfn "Prev: %s, Cur: %s, Next: %s" prev cur next print_dbl_list x.Next let my_dbl_list = None |> dbl_cons 1 |> dbl_cons 2 |> dbl_cons 3 |> dbl_cons 4 print_dbl_list my_dbl_list
Output is:
Prev: --, Cur: 4, Next: 3
Prev: 4 , Cur: 3, Next: 2
Prev: 3 , Cur: 2, Next: 1
Prev: 2 , Cur: 1, Next: --
The mutable field here has been bugging the daylights out of me, and I almost gave up on trying to find a way to get O(1) inserts while not making the list destructive. I finally hit on the winning idea, but it comes at a tradeoff: Namely, space. Typically a doubly linked list uses O(n) space (3 fields per element of the list), and this approach uses O(n^2) space. Instead of keeping a simple reference to the previous object and a reference to the next object, it keeps an entire list for both the next and an entire list for the previous. So conceptually if you have this list:
A -> B -> C -> D
then the nodes are as follows:
A : Next = [ B; C; D ] Prev = []
B : Next = [ C; D ] Prev = [ A ]
C : Next = [ D ] Prev = [ B; A ]
D : Next = [] Prev = [ C; B; A ]
Notice that the Prev list is in reverse. The advantage to this approach is that when inserting at an arbitrary location, you can easily construct the new prev and next values by simply manipulating the lists.
I think it's probably impossible to achieve O(1) insertions using a recursive data structure, and as such the techniques in the link you mentioned I don't think are going to be practical. I'd love for someone to prove me wrong, because it looks like a neat technique, but conceptually I just can't see how it's possible to implement a purely function doubly linked list using a recursive data structure in any language and have inserts be O(1).
Anyway, here's the code for my new attempt. I wrote it as a discriminated union with member functions, since I'm not familiar with module syntax yet (still only on chapter 6 of Expert F#)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
type 'a DblList = | None | Some of ('a list * 'a * 'a list) member x.Next() = match x with | None -> None | Some(_, _, []) -> None | Some(prev, cur, next::tail) -> Some(cur::prev, next, tail) member x.Prev() = match x with | None -> None | Some([], _, _) -> None | Some(prev::prevtail, cur, next) -> Some(prevtail, prev, cur::next) member x.Length() = match x with | None -> 0 | Some(_, _, next) -> 1 + List.length next member x.TotalLength() = match x with | None -> 0 | Some(prev, _, next) -> 1 + List.length next + List.length prev member x.InsertBefore(value:'a) = match x with | None -> Some([], value, []) | Some(prev, cur, next) -> Some(prev, value, cur::next) member x.InsertAfter(value:'a) = match x with | None -> Some([], value, []) | Some(prev, cur, next) -> Some(cur::prev, value, next) member x.ScanToHead() = match x with | None -> None | Some([],_,_) -> x | Some(_,_,_) -> x.Prev().ScanToHead() override x.ToString() = match x with | None -> String.Empty | Some(_, v, []) -> v.ToString() | Some(_, v, _) -> v.ToString() + ", " + x.Next().ToString()
Good attempt. The problem with this solution though is not that it uses O(n^2): it doesn't, it only uses O(n) as expected. F# lists are linked lists not arrays so you do not copy the old list when you cons a new element onto the front. So each call to your insert functions is reusing the (singly-linked) lists from the old doubly-linked list and not reallocating them. Hence making the insert memory performance O(1), which is expected.
The problem with this solution is when traversing the list. Your Prev and Next operations do allocate memory, which is bad. Each Prev/Next operation allocates a Some
cell and a (::)
cell, making their memory allocation cost O(1), not O(0) as you'd expect. So when you iterate through this doubly-linked list you are actually consuming memory. This is not actually a huge problem because, provided that you don't hold on to the lists from old iterations, this memory will churn quickly through the GC. But it is still not a desirable situation.
Generally to implement a circular data structure like a doubly-linked list you need one of two things: mutation or laziness. Your first solution uses mutation. Here is a solution that uses laziness.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
let force (x:Lazy<_>) = x.Force() let rec fix f = f (lazy (force (fix f))) type DblList<'a> = Lazy<Cell<'a> > and Cell<'a> = DEmpty | DJoin of DList<'a> * 'a * DList<'a> let empty () = lazy DEmpty let join prev x next = lazy DJoin (prev, x, next); let before x dl = match force dl with | DEmpty -> join (empty ()) x (empty ()) | DJoin (prev, y, next) -> fix (fun curr -> join prev x (join curr y next))
When inserting an element into the list it uses the fix-point combinator to build the current node of the list in terms of itself. If F# had special "let rec" syntax for lazy values then you might imagine this line being written
1
let rec curr = join prev x (join curr y next))
The fix
function is just like a poorman's "let rec".
The problem with this solution is when traversing the list. Your Prev and Next operations do allocate memory, which is bad. Each Prev/Next operation allocates a
Some
cell and a(::)
cell, making their memory allocation cost O(1), not O(0) as you'd expect. So when you iterate through this doubly-linked list you are actually consuming memory. This is not actually a huge problem because, provided that you don't hold on to the lists from old iterations, this memory will churn quickly through the GC. But it is still not a desirable situation.
Hi, I have a question. Using the lazy construct, don't we also have some memory overhead as well in the form of lamba(or is it thunk) ? I tried to expand based on your example. Please comment as I am still learning
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
let force (x:Lazy<_>) = x.Force() let rec fix f = f(lazy (force (fix f))) type DList<'a> = Lazy<Cell<'a> > and Cell<'a> = DEmpty | DJoin of DList<'a> * 'a * DList<'a> let empty () = lazy DEmpty let join prev x next = lazy DJoin (prev, x, next); let prev dl = match force dl with | DEmpty -> failwith "empty list" | DJoin (prev, y, next) -> match force prev with | DEmpty -> failwith "already at head" | DJoin (prev, y, next) -> join prev y dl let next dl = match force dl with | DEmpty -> failwith "empty list" | DJoin (prev, y, next) -> match force next with | DEmpty -> failwith "already at end" | DJoin (prev, y, next) -> join dl y next let before x dl = match force dl with | DEmpty -> join (empty ()) x (empty ()) | DJoin (prev, y, next) -> fix (fun curr -> join prev x (join curr y next)) let after x dl = match force dl with | DEmpty -> join (empty ()) x (empty ()) | DJoin (_prev, y, _next) -> next (fix (fun curr -> join _prev y (join curr x _next))) let elem dl = match force dl with | DEmpty -> failwith "empty list" | DJoin (prev, y, next) -> y let rec fst dl = match force dl with | DEmpty -> dl | DJoin (p, y, n) -> match force p with | DEmpty -> dl | _ -> fst (prev dl) let rec last dl = match force dl with | DEmpty -> failwith "empty list" | DJoin (p, y, n) -> match force n with | DEmpty -> dl | DJoin(p',_,n') -> last n let length dl = let rec _lenn dl acc = match force dl with |DEmpty -> acc |DJoin (prev, y, next) -> _lenn next (acc + 1) let rec _lenp dl acc = match force dl with |DEmpty -> acc |DJoin (prev, y, next) -> _lenp prev (acc + 1) match force dl with | DEmpty -> 0 | DJoin(prev, y, next) -> _lenp prev 0 + _lenn next 0 + 1 let pop dl = match force dl with | DEmpty -> failwith "empty list" | DJoin(prev, y, next) -> match force prev with |DEmpty -> (y, next); |DJoin(p,x,n) -> (y, join p x next);
Yes, the lazy construct forms thunks which are only evaluated once. If you want to track memory usage you can count the number of times a data constructor (DEmpty or DJoin) is called. It is easy in this case because we already have helper functions to build these data values so you can just stick a counter increment in there. E.g.,
1 2
let callCount = ref 0 let join prev x next = incr callCount; lazy DJoin (prev, x, next)
Then read callCount after you have run some test.
I'm not sure what your prev
and next
functions are meant to do, but if they are meant to advance forward and backward through the list then I would have them work like this (where they return DEmpty if you go off the end of the list).
1 2 3 4 5 6 7 8 9 10
let next dl = match force dl with | DEmpty -> lazy failwith "empty list" | DJoin (_, _, next) -> next
after
I would write:
1 2 3 4 5 6 7 8 9 10
let after x dl = match force dl with | DEmpty -> join (empty ()) x (empty ()) | DJoin (prev, y, next) -> fix (fun curr -> join (join prev y curr) x next)
fst
/last
are ok. They are doing a redundant pattern match on each recursion though (e.g., on the recursive call to last, n is already known to be DJoin, but it is tested against DEmpty when it recurses (and I don't think the compiler will optimize this for you)). So I would prefer to write fst
as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
let first dl = let rec first' curr last = match force curr with | DEmpty -> last | DJoin (prev, _, _) -> first' prev curr first' dl (lazy failwith "empty list")
thanks.
At first, I wrote prev/next as what you have shown but some how, I cannot do the reverse, i.e. I cannot satisfy the following :
Assert(next (prev dl) == dl)
Which is why I wrote it like it is now and also my question of even I am scanning through the list, in order to maintain the above, I need to reconstruct another thunk. May be I have missed something though.
It seems that forcing laziness into a non-lazy language is quite complex.
I was hoping that I woke up in time to correct the comment about the O(n^2) space, because I also realized that it's indeed O(n) space a little while later :P The code fragment you've given is a little over my head, but maybe in a few weeks or months after I learn more about F# it will make sense. Thanks!
The code shouldn't be all that hard given that you know already the basics of F#: disciminated union data types, pattern matching, etc. If you don't already know how the Lazy type works in F# then that's something you should learn first. After that don't worry about how the fix function works -- that is hard. Instead just appreciate how the fix function is used. If you see a line
1
fix (fun x -> ... x ...)
and you squint and tilt your head it actually looks like
1
let rec x = ... x ...
so intuitively you are just defining a value in terms of itself. As you commonly do for functions.
While I think about the "fix" function here is the solution I reached over the weekend after looking at pg 220 of "Expert F#":type tdbllist =
| Nil
| Nod of tdbllist*int*tdbllist
| Delay of Lazy<tdbllist>
let rec mkdbllist (lst:tdbllist) x =
match x with
| h::t -> let rec n() = let m = Delay( lazy( mkdbllist (n()) t))
Nod(lst, h, m)
n()
| [] -> Nil
let y = mkdbllist Nil [1;2;3;4;5]
Graeme
Here is a print function:let rec printright node prev =
match node with
| Nod(l,i,r) -> printf "pr item %d\n" i
printright r node
| Delay(d) -> printright (d.Force()) prev
| _ -> prev
Thanks for the feedback
Graeme
Topic tags
- f# × 3705
- websharper × 1897
- compiler × 286
- functional × 201
- ui next × 139
- c# × 121
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 76
- ui.next × 67
- bug × 54
- core × 49
- website × 49
- server × 45
- parallel × 43
- ui × 43
- enhancement × 41
- parsing × 41
- testing × 41
- trywebsharper × 41
- typescript × 37
- html × 35
- javascript × 35
- owin × 35
- asynchronous × 30
- monad × 28
- ocaml × 28
- tutorial × 27
- warp × 27
- haskell × 26
- sitelet × 25
- linq × 22
- workflows × 22
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- sitelets × 19
- monodevelop × 17
- rpc × 17
- suave × 17
- piglets × 16
- collections × 15
- feature request × 15
- jquery × 15
- templates × 15
- getting started × 14
- pipeline × 14
- kendoui × 13
- reactive × 12
- 4.1.0.171 × 11
- monads × 11
- opinion × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- formlets × 9
- in × 9
- json × 9
- plugin × 9
- proposal × 9
- scheme × 9
- solid × 9
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- python × 8
- 4.1.1.175 × 7
- complexity × 7
- documentation × 7
- visual studio × 7
- 4.1.2.178 × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- remoting × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- authentication × 5
- azure × 5
- bootstrap × 5
- conference × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- released in Zafir.4.0.188.91-beta10 × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.6.118 × 4
- css × 4
- example × 4
- extensions × 4
- fsi × 4
- fsx × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- reflection × 4
- remote × 4
- rest × 4
- spa × 4
- teaching × 4
- template × 4
- websocket × 4
- wontfix × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.11.258 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- database × 3
- erlang × 3
- events × 3
- extension × 3
- file upload × 3
- forums × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- svg × 3
- type provider × 3
- view × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- android × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- d3 × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- docs × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- google maps × 2
- hosting × 2
- http × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- performance × 2
- pipelines × 2
- q&a × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websharper4 × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- Fixed in 4.0.190.100-rc × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- WebComponent × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- api × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data × 1
- data grid × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- dom × 1
- domain × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- google × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- how to × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- maps × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- post × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- qna × 1
- quant × 1
- query sitelet × 1
- question × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- sweetalert × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio shell × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
![]() |
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
Has anyone managed to create a doubly linked list in F# using techniques such as are described in [link:www.csse.monash.edu.au] ?
Thanks