5 ms·
Really? How beautiful it is is exactly what keeps drawing me to Haskell even though I have more invested in the dynamic language camp. >max = head . sort Due
by loewenskind 16y ago
Really? How beautiful it is is exactly what keeps drawing me to Haskell even though I have more invested in the dynamic language camp.
>max = head . sort
Due to the laziness, the above will find the max entry in O(n) time, just like your hand written loop would. How can you not find that beautiful?
Now I do agree that they often seem to use too many symbols that look like other symbols but the few times I've investigated it actually ended up making sense (e.g. Arrows).
- Flow 16y agoYou mean max = last . sort ?
- loewenskind 16y agoNo, using head is what causes the result to be O(n) even though sorting should be more complex. By only using the first entry, only the first entry will actually be found by the sort. I assume you're pointing out that I have my sort backwards but I was being intentionally as ambiguous with this part because it's not relevant to the point I was making.
- Flow 16y agoI understand that lazyness could make it only sort as much as needed to get the first element in the sorted list. Yes, it's triggers my Rainman instincts to point out that 1. It should be max = last . sort 2. Or min = head . sort 3. You are making assumptions on the sorting algorithm to make it possible to short-cut the evaluation. K-MART! K-MART! :) Edit: How nice of you to downvote...
- loewenskind 16y agoIt wasn't me who downvoted. I can't (since you responded to me) and wouldn't have anyway. If you use last then that means the whole list will have to be sorted O(n log n ish) then the last element retrieved. And yes, I'm assuming a merge sort. But I still don't think that detracts too much from my point.
- Flow 16y agoCould you please s/max/min/ in your first post?
- deleted 16y ago[deleted]
- loewenskind 16y agoBut it depends on the sort algorithm. In the exercise I did this on (in Ocaml with some custom lazy code, since Ocaml's wasn't lazy enough for what I was doing) I did sort in descending order.
- Flow 16y agoAre you saying you made a sort function that sorts reversed, and named it sort? What's wrong with sortDesc? I think you should be more careful with following conventions. Everyone knows sort sorts ascending.
- loewenskind 16y agoConceded. But I can't edit my original comment now. :(
- ithkuil 16y agoI love haskell and understand your point. Laziness is beautiful and allows to express things in a simple way and also improve the reusability of the code etc. However I think that you didn't pick the best example. I tried out: >head $ sort [1 .. 10000000] 6 secs, using ~2g of heap! (actually is should be a reverse sort) and the "hand coded loop": >let mx (x:xs) m = if x > m then mx xs x else mx xs m; mx [] m = m >mx [1 .. 10000000] 0 3 secs, heap usage stays negligible low and constant. Something is clearly not behaving as you depicted. (of course my 'loop' code is not the exact equivalent of the last.sort composition, since it requires a 'minimum' parameter to be passed in advance, which not all types have. On the other hand it works also for the empty list) I also have the feeling that, unless special compiler optimization (a very 'specific' one, I fear), the simple application of the 'head' function to a sorted list would stop when the first result element is produced, which is not after O(n). Granted, you don't have to wait for a full sort, since the sorting algorithm could guarantee that the rest of the list contain 'lesser/bigger' elements only and thus stop. But keeping track of all this should be space consuming, in respect to a simple linear scan. Anyway the space problem of the "lazy" solution is a bigger issue than the number of comparisons. I'm not a haskell master, but if I got it right, one of the mayor problems of lazy programming is that in some situations it can degenerate to a huge amounts of "unevaluated thunks", which are frozen computations yet to be performed, but which require some state to be held in memory (like function arguments, I guess). (http://www.haskell.org/haskellwiki/Thunk http://www.haskell.org/haskellwiki/Thunk) or in this case, the space usage is caused simply because the list has to be materialized in memory instead of be simply traversed and generated on the fly. (but 2g seems slightly too much). Anyway, the point here is that the two methods are not equivalent. I think that understanding the impact of laziness on space is an issue that certainly increases the learning curve, as it requires time to master this and other optimization techniques if you want to get predictable performances from haskell. (BTW, I actually use haskell for work, perhaps in a slightly conterintuitive way. I use haskell for quick prototyping ideas and solutions. Sometimes I get inspired by the solution I end up with haskell and translate it in clojure or java (work requirement), or at other times I have to rewrite it completely, but the possibility to quickly prototype in haskell really helps me a lot. I would love a stable ghc JVM backend.... it would change my life)
- ithkuil 16y ago