My get-started example is the implementation of Norvig's toy spelling corrector.
What a wonderful example program! This is so enticing that I can't help but have a go myself.
My first impressions are that Peter's Python implementations are not a good starting point for several reasons. The most important of which is that it is very idiomatic Python, to the extent that the program appears to be heavily optimized for an interpreted language like Python. I think F# can do vastly better but you'll have to start from scratch with a completely different approach.
My second concern is that I believe the algorithm could be better. Rather than searching for single or double errors, it should search for all errors but bias the search towards the highest probability errors. For example, the error from "ace" to "oace" is probably much less likely than the error "tomorrow" to "tommorow" despite the fact that the latter involves two separate errors.
I'll let you know how I get on.
Cheers,
Jon.
Hi Sebastian,
It's a beautiful program!
I ran the program and got 2.8s for "something". My laptop is fairly quick though. But I'm certain we could do a lot better. Could you send us the full C# and F# code to fsbugs AT microsoft.com?
Sequence expressions are odd with regard to performance. The aim of the first release of the feature (in December 06) was absolutely to "get the feature in there in time for the books", with a focus on getting some (but not all) of the performance cases right. Since then we've mainly been fixing correctness problems (e.g. with regard to Dispose of database connections). But we'll certainly look into this particular issue.
"yield" is quite magical in C#: it's imperative programming on steroids. It's not the right model for F#, but it is an amazing construct. While the two look similar, the compilation strategies are indeed wildly different, and the C# team has a 6 year lead over us in terms of implementation effort on this one. So this is one area where I expect the "natural equivalent" C# program to currently be faster.
You can use Seq.map and Seq.filter over enormous sequences and things work out pretty well. However the sequence expression model is harder to compile than C# iterators, so I think that in this case the C# iterator code will always be at least as fast as the F# code. Also, I suspect most F# programmers intuitively switch to working over concrete data structures (arrays and lists) when performance really matters. I'm not sure if that's possible here.
Regards,
Don
Sorry - have no reproduced your perf problem - I was using
1
let (|..>) x y = Seq.map_concat y x
instead of
1
let (|..>) x y = Seq.map y x
We'll look into where the perf problems are.
regards,
don
We'll look into this further over the next few days. For starters, Seq.map_concat is considerably faster than your fold/append/concat combination. A very long chain of Seq.append's causes problems. This is exactly the sort of situation where C# iterators do better, since they compile to a state machine.
Also removing the "Set.of_seq" in "edits" makes things faster. But that's more of an algorithmic issue.
Am I right in thinking this is logically equivalent code?
1 2 3 4 5 6 7
let edits (word: string) = { for i in {0 .. word.Length-1} -> word.Substring(0,i) + word.Substring(i+1) } +.. (* delete *) { for i in {0 .. word.Length-2} -> word.Substring(0,i) + word.Substring(i+1,1) + word.Substring(i,1) + word.Substring(i+2) } +.. (* transpose *) { for i in {0 .. word.Length-1} for c in {'a' .. 'z'} -> word.Substring(0,i) + (String.make 1 c) + word.Substring(i+1) } +.. (* alter *) { for i in {0 .. word.Length} for c in {'a' .. 'z'} -> word.Substring(0,i) + (String.make 1 c) + word.Substring(i) } (* insert *) |> Set.of_seq let edits2 word = Set.of_seq (Seq.map_concat edits (edits word))
Regards,
don
"A very long chain of Seq.append's causes problems"
This is definitely true, as you must have found when you removed the thousands of appends implied by edit2. When I replace the guts of edit2 with a Seq.map_concat, the generation of edits2("something") takes a little under 5 seconds... something like 10 or 15 times faster. Certainly the state machine implementation of Seq.append is trivial in C# with the magical yield operator.
1 2 3 4 5 6 7 8 9
public static IEnumerable<T> Sequence<T>(params IEnumerable<T>[] es) { int i = 0; foreach (IEnumerable<T> e in es) { foreach (T t in e) yield return t; es[i++] = null; // oh why not be nice to the garbage collector } }
I see that the underlying implementation of most of the seq functions is inherently stateful, and perhaps I am glossing over too many details, but the F# doesn't look that different. The performance drop is surprising. Perhaps because it is creating so many more enumerator objects instead of surfing through the one it started with? Anyway, fascinating.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
let append (ie1: #IEnumerable<'a>) (ie2: #IEnumerable<'a>) = generate // the state holds two enumerators: one for the outer list and one for the current inner list (fun () -> (ref (ie1.GetEnumerator()), ref false)) (fun (curr,right) -> let rec take () = // check the inner list match get !curr with | None -> // check the outer list if !right then None else // don't forget to dispose of the enumerator for the inner list now we're done with it (!curr).Dispose(); curr := ie2.GetEnumerator(); right := true; take () | res-> res take ()) (fun (curr,_) -> (!curr).Dispose())
Hi Sebastian,
I took a bit more of a look at this. I came up with a way to optimize Seq.append under the hood - it reduces runtimes dramatically. The overhead then becomes the use of Sets to implement "distinct" - I think a HashSet would be better here. When the use of sets is removed I get a runtime of 0.8s, but I'm certain further optimizations are possible.
The optimization is as follows. The problem with a naive implementation of a binary "append" operator on IEnumerables is that a long chain of appends creates a long chain of cascading IEnumerators - if you have a chain of length N and yur getting the last element then your still asking the IEnumerator for the top most "append", which asks teh IEnumerator for the next-top-most "append" etc. all the way down the chain. Thus iterating becomes quadratic.
The trick is to represent the results of Seq.append symbolically, rather than by creating an object that represents a fixed strategy for how the resulting IEnumerable will be implemented. If a zillion Seq.appends are then concatenated then you can optimize the symbolic representation (by concatenating all the sub-elements to append) and when you actually come to iterate you just use an integer state index to record which node you're yielding from. Effectively we're working out the best way to record the state "behind the scenes". I'd say this can be generalized to further behind-the-scenes deforesting, for example "map f (map g E)" can become "map (f o g) E". All very interesting. Here are some code snippets (note the use of active patterns to factor out some bits :-) ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
type ScriptedEnumerator<'a>(script: Script<'a>) = class interface IEnumerable<'a> with override x.GetEnumerator() = script.GetEnumeratorForScript() interface IEnumerable with override x.GetEnumerator() = (script.GetEnumeratorForScript() :> IEnumerator) member x.Script = script end let (|HasScript|) (x : #IEnumerable<'a>) = match (x :> IEnumerable<'a>) with | :? ScriptedEnumerator<'a> as s -> s.Script | ie -> Run(ie) let append (HasScript(s1)) (HasScript(s2)) = (new ScriptedEnumerator<'a>(Script.MkAppend(s1,s2)) :> seq<'a>)
And the bit that interprets scripts (note: uses of "generate" are always a bit ugly - the downside of not having "yield", but then this goes in the library, right!)
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
type Script<'a> = | Append of Script<'a> [] | Run of IEnumerable<'a> with static member MkAppend(s1,s2) = let (|Flat|) = function Append(arr) -> arr | x -> [| x |] match s1,s2 with | Flat(arr1),Flat(arr2) -> Append(Array.append arr1 arr2) // based on Seq.concat member script.GetEnumeratorForScript() = match script with | Append(arr) -> IEnumerator.generate // the state holds two enumerators: one for the outer list and one for the current inner list (fun () -> (ref 0, ref (IEnumerator.empty()))) (fun (n, curr) -> let rec take () = // check the inner list match get !curr with | None -> // check the outer list if !n >= arr.Length then None else let subScript = arr.[!n] incr n; // don't forget to dispose of the enumerator for the inner list now we're done with it (!curr).Dispose(); curr := subScript.GetEnumeratorForScript(); take () | res-> res take ()) (fun (iese,curr) -> (!curr).Dispose()) | Run(ie) -> ie.GetEnumerator() end
Your changes to the edit function look right to me. It's always a fun tradeoff with these things knowing where to "stop the seq train". having edit produce a set rather than a stream of unique words makes as much sense as anything. I realized later I had not posted the |..> operator I toyed with. In my case it was
1
let (|..>) a b = a |> Seq.map b
I will email you the entire code separately, but I've pasted it in below for those playing along at home. I agree with the statement that yield is imperative programming on steroids. It's a wonderful construct and very intuitive for most imperative programmers, once they get over the shock of having the compiler write non-trivial code for them.
1 2 3 4 5 6 7 8 9 10 11 12
#light let (|..>) a b = a |> Seq.map b let (+..) = Seq.append let edits (word: string) = { for i in {0 .. word.Length-1} -> word.Substring(0,i) + word.Substring(i+1) } +.. (* delete *) { for i in {0 .. word.Length-2} -> word.Substring(0,i) + word.Substring(i+1,1) + word.Substring(i,1) + word.Substring(i+2) } +.. (* transpose *) { for i in {0 .. word.Length-1} for c in {'a' .. 'z'} -> word.Substring(0,i) + (String.make 1 c) + word.Substring(i+1) } +.. (* alter *) { for i in {0 .. word.Length} for c in {'a' .. 'z'} -> word.Substring(0,i) + (String.make 1 c) + word.Substring(i) } (* insert *) |> Set.of_seq |> Set.to_seq let edits2 word = Set.of_seq (Seq.fold Seq.append Seq.empty (edits word |..> edits)) printf "%d" (Seq.length (edits2 "something"))
and the C# below, including the "F" class referenced within the code. With LINQ, much of the "F" class withers away in favor of the native implementations of the same concepts, but I haven't quite gotten there yet.
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
class Program { // what a shame you cannot yield from an anonymous function // or this would be inside Edits private static IEnumerable<string> EditsRaw(string word) { for(int i=0; i < word.Length; ++i) yield return word.Substring(0, i) + word.Substring(i + 1); for(int i=0; i < word.Length-1; ++i) yield return word.Substring(0, i) + word.Substring(i + 1, 1) + word.Substring(i, 1) + word.Substring(i + 2); for(int i=0; i < word.Length; ++i) for(char c='a'; c <= 'z'; ++c) yield return word.Substring(0,i) + c.ToString() + word.Substring(i + 1); for(int i=0; i <= word.Length; ++i) for(char c='a'; c <= 'z'; ++c) yield return word.Substring(0, i) + c.ToString() + word.Substring(i); } private static IEnumerable<string> Edits(string word) { return F.Distinct(EditsRaw(word)); } private static IEnumerable<string> Edits2Raw(string word) { foreach(string edit in Edits(word)) foreach(string edit2 in Edits(edit)) yield return edit2; } private static IEnumerable<string> Edits2(string word) { return F.Distinct(Edits2Raw(word)); } static void Main(string[] args) { Console.Out.WriteLine(F.Count(Edits2("something"))); } } public static class F { public static IEnumerable<T> Distinct<T>(IEnumerable<T> from) { if (from == null) yield break; Dictionary<T, byte> values = new Dictionary<T, byte>(); foreach (T t in from) { if (!values.ContainsKey(t)) { values.Add(t, 0); yield return t; } } } public static int Count<T>(IEnumerable<T> from) { int i = 0; foreach(T _ in from) ++i; return i; } }
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 |
I hope I'm doing something wrong, but I'm seeing some poor performance in comprehensions that's a little unexpected. I'm a long time Scheme and C# programmer, so I feel like I should be happy as a pig in slop in the F# world! My get-started example is the implementation of Norvig's toy spelling corrector. I figure it's a nice simple example perfect for seqs and comprehensions. Here is two of the basic routines: edits is given a word, it determines all the words which are one spelling error away from that word. edits2 determines all the words two spelling errors away
Works like a charm, reads gracefully, fun to program ,etc. It takes about 60 seconds to produce all the edit2 results for "something"... about 140k words. It thought it seemed a bit pokey, though. So I coded up what appears to be a nearly equivalent implementation in C# and it produces the answer in somewhere on the order of half a second. I am not clear on what the big performance drain is.
(The F class contains some generic IEnumerable based idioms I've had in my back pocket the last couple of years. Their implementation looks pretty much exactly like the stuff in the FSharp.Collections library.) I know that earlier versions of F# discussed fast comprehensions on integer ranges, but I don't see much evidence of that in the generated IL. I wonder if there is some subtlety in the implementation that I am missing, because I keep hearing stories about excellent F# performance stats. Thanks for any advice you can give me!