TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Programming and thinking the functional way

66 点作者 peteratt大约 11 年前

14 条评论

tel大约 11 年前
I still remember the first time I started to grok recursive definitions in a functional language. At the time it was Scheme and I remember going through all the effort to track the various bits of state and operation as they &quot;reboot&quot; and begin again in each recursive call.<p>It made me think that recursion, no matter how short the code looked, was terrible.<p>Now I realize that recursion, thought about properly, requires so much less mental overhead. All those lines of code that vanished were really whole concepts and ideas which could vanish as well.<p>Of late, my style has been refined by working with theorem provers which make the ultimate connection. Recursion is absolutely nothing more than mathematical induction. Each recursive call is just you invoking the inductive hypothesis.<p>What that connection really drives home is that when writing a recursive definition you need to do so very few things. First, you need to make sure your definition has a good foundation—you must list all of the conditions where it terminates and ensure that they truly found and support the algorithm&#x2F;proof. Then you must write the inductive engine that will burn away whatever inputs you receive down to the foundation you just made.<p>The release is in noticing that those inductive engines can be primed on the very tiniest actions—and those tiny actions are all you, the implementer, are responsible for.<p>When writing your inductive step consider only the things that are novel about <i>this case</i>. If I&#x27;m working with an input list and inducting on `cons` then all I must do is consider what happens to the head and tail of the list. Everything else will be handled by the turning of the inductive engine.<p>Now a good algorithm just feels like someone walked me into a room with a gigantic domino structure. I walk around it slowly and determine the ends and then just flick the single domino which will churn away inevitably the rest of the algorithm.
评论 #7712230 未加载
rdtsc大约 11 年前
Another practical functional language is Erlang.<p>It is at the core of many mobile to internet gateways out there. At the core of WhatsApp. Some databases (Riak, CouchDB) and message queues (RabbitMQ).<p>Language-wise, besides concurrency constructs, you get pattern matching, immutable data structures (and bindings). Unlike Haskell, all types are dynamic (but strong).<p>Also a counterpart to Learn You A Haskell For Great Good is Learn You Some Erlang For Great Good:<p><a href="http://learnyousomeerlang.com/syntax-in-functions#pattern-matching" rel="nofollow">http:&#x2F;&#x2F;learnyousomeerlang.com&#x2F;syntax-in-functions#pattern-ma...</a><p>For example the quicksort sample would look like:<p><pre><code> sort([Pivot|T]) -&gt; sort([ X || X &lt;- T, X &lt; Pivot]) ++ [Pivot] ++ sort([ X || X &lt;- T, X &gt;= Pivot]); sort([]) -&gt; []. </code></pre> But it is the large applications that ends up breaking my brain. I am convinced once you learn programming in an imperative or object oriented language you brain just become molded to that model of thinking and it is very hard to adjust (it is nevertheless very useful to try).
评论 #7711954 未加载
评论 #7711750 未加载
cousin_it大约 11 年前
It boils down to the choice between these two alternatives:<p>1) If it&#x27;s simple to imagine the computer doing something, it should be easy to program.<p>2) If it&#x27;s simple to analyze something mathematically, it should be easy to program.<p>The first path leads to C-style programming with pointers and loops. The second path leads to lazy pure functional programming and beyond.<p>Personally, I&#x27;m in the first camp. Small functional programs are often shorter and more beautiful than their imperative and OO counterparts, but large functional programs quickly accumulate so many abstractions that they become incomprehensible to me, more so than large imperative or OO programs.<p>Maybe that just says something about my intelligence, or maybe the functional camp hasn&#x27;t yet figured out how to write large programs in a readable way. Case in point, see the list of operators defined by the popular Lens library in Haskell: <a href="http://hackage.haskell.org/package/lens-3.8.5/docs/Control-Lens-Operators.html" rel="nofollow">http:&#x2F;&#x2F;hackage.haskell.org&#x2F;package&#x2F;lens-3.8.5&#x2F;docs&#x2F;Control-L...</a>
评论 #7711639 未加载
评论 #7711625 未加载
评论 #7711601 未加载
评论 #7711567 未加载
评论 #7711710 未加载
评论 #7711532 未加载
keeg大约 11 年前
Writing quicksort non-functionally doesn&#x27;t HAVE to be unreadable. Not that I disagree with Haskell being great and all, I just think it&#x27;s important to realize that imperative != ugly code.<p><pre><code> public class QS { public List&lt;T&gt; Quicksort&lt;T&gt;(List&lt;T&gt; l, Comparator&lt;T&gt; comp) { if (l.size() &lt;= 1) { return l; } List&lt;T&gt; lesser = new ArrayList&lt;T&gt;(); List&lt;T&gt; greater = new ArrayList&lt;T&gt;(); T pivot = l.get(0); for (T t: l) { if (comp.compare(pivot, t) &lt;= 0) { lesser.add(t); } else { greater.add(t); } } List&lt;T&gt; out = new ArrayList&lt;T&gt;(); out.addAll(Quicksort(lesser)); out.addAll(Quicksort(greater)); return out; } }</code></pre>
评论 #7711560 未加载
评论 #7711435 未加载
eoconnell大约 11 年前
&quot;Wow. i&#x27;s and j&#x27;s all the way, what is this? Why is it so long compared to the Haskell example? This looks like comparing C and assembly 30 years ago! And in some respects, it is the same leap.&quot;<p>I don&#x27;t think this is a fair comment considering the in-place Haskell implementation isn&#x27;t incredibly readable either.
评论 #7711580 未加载
评论 #7712193 未加载
ridiculous_fish大约 11 年前
I&#x27;m not sure if I&#x27;m the first to notice, but the Haskell quicksort function is wrong, because it mishandles NaN:<p><pre><code> main = let nan = 0.0 &#x2F; 0.0 in do putStrLn $ show $ quicksort [nan, 1.0, 2.0, 3.0] putStrLn $ show $ quicksort [1.0, nan] [NaN] [1.0] </code></pre> Sort routines should not return a list of a different length than their input.
评论 #7712358 未加载
alkonaut大约 11 年前
So a divide-and-conquer algorithm on collections is more elegant functionally than imperatively? Also: water found wet.<p>Haskell is elegant &amp; Java isn&#x27;t, but cherry picking examples always comes with a risk of making your argumentation straw-man-ish.<p>Would be interesting to see some examples where imperative isn&#x27;t so horrible, and how Haskell compares. The in-place sort the author mentions, for example.
评论 #7713056 未加载
ExpiredLink大约 11 年前
The Java example builds up a straw man. Never underestimate your readers!
评论 #7712865 未加载
评论 #7711619 未加载
thinkpad20大约 11 年前
Ah, the ole &quot;quicksort in 3 lines&quot; argument. There are a few things I take from this.<p>The good:<p>1) The definition of the algorithm is clear. It shows &quot;how quicksort works.&quot;<p>2) It&#x27;s trivial to see (and prove) that the function will terminate, and almost as trivial to prove that it will result in a sorted list. So, it is easy to show correctness.<p>3) The polymorphism makes this an easily reusable function right out of the box.<p>The bad:<p>1) That implementation is very inefficient. At a glance I think it would be O(n^2) time. (Edit: this is misleading, because it&#x27;s only O(n^2) in the same way that quicksort is always O(n^2). It is inefficient in terms of memory usage, though. And possibly other ways; for instance, I&#x27;m not sure how laziness would affect this. But I don&#x27;t want to be spreading FUD...)<p>2) The &quot;efficient&quot; implementation given at the bottom is just as inscrutable as any other quicksort implementation I&#x27;ve seen. More so because of the monadic code, single-letter variables (pr?) and opaque library function calls (unsafePartition?) being made. And I&#x27;m not even sure that it would work on a list, although since V.Vector appears to be a type class, perhaps list is an instance of it.<p>3) Both the efficient and inefficient implementations are concise in large part because of their use of library functions. This is often a good thing: Haskell provides a great way to abstract things because of its parametric and ad-hoc polymorphism, and allows for a lot of reusable code. But it comes at a cost too, which is that the actual instructions you&#x27;re giving to the machine are very far removed from what the computer is doing. Who knows how much code is <i>actually</i> executed, how deep the rabbit hole goes, to translate those beautiful 4 lines into actual machine instructions? With Java, it&#x27;s precisely visible what the machine is doing to execute your code. This is much less the case with Haskell.<p>I suppose you could say (broadly) that functional languages excel at expressing <i>what</i> your program should do, while imperative languages excel at expressing <i>how</i> your program should do it. There are cases when you care more about the former, and cases when you care more about the latter. The reason I take issue with the quicksort example, is that list-sorting is a case where you <i>definitely</i> care more about the <i>how</i> than the <i>what</i>.<p>-------------------<p>EDIT, since two people called me out on it: It was an overstatement on my part to say that it&#x27;s &quot;precisely visible&quot; what your Java code will translate to, but to suggest the two languages have the same degree of abstraction from the CPU is ridiculous. The code translation from Java to bytecode is quite straightforward, because most of the optimization occurs at runtime with JIT. There is a reasonably direct relationship between the code you write, the bytecode it gets translated into, and the instructions executed at runtime. At the end of the day, Java code consists of a series of instructions for the computer to follow, while on the other hand in Haskell, you aren&#x27;t even technically giving instructions at all -- you&#x27;re just writing equations. Compiled Haskell code is completely inscrutable.<p>Also, I should note that I am an enthusiastic Haskell hobbyist, and write it on almost a daily basis. Although I might not come across it here, I am a huge fan of the language; outside of the languages I use at work, by far the one I use most is haskell.
评论 #7711975 未加载
评论 #7711812 未加载
评论 #7711781 未加载
评论 #7712989 未加载
评论 #7712048 未加载
评论 #7711872 未加载
评论 #7711905 未加载
skybrian大约 11 年前
After rewriting Collections.sort() just for the fun of it, the claim that &quot;an hour of a developer&#x27;s time is a lot more expensive than an hour of a high-performance AWS super-duper-cluster instance&quot; isn&#x27;t all that convincing. If you&#x27;re going to rewrite sort at all, you should take time to do it right, and the Stream-based version isn&#x27;t it.
评论 #7711987 未加载
elwell大约 11 年前
Creating an entire class for the Java version is kind of a disingenuous comparison.
评论 #7711605 未加载
noname123大约 11 年前
Hi, was wondering if functional language and Java peeps can speak to the performance Java 8&#x27;s stream(...); curious if the performance of using lambda expressions hold up against say using the old for loops and also whether they do any caching or build internal lookup tables when you do anyMatch(...) or something similar on a subfield of a object.<p>Also for any C# peeps who already have had experiences using lambda expressions on the awesome .net platform. How was performance there?
_random_大约 11 年前
No need to switch to an alien language, just opt for C#&#x2F;F# for a nice middle ground:<p><a href="http://fsharpforfunandprofit.com/posts/fvsc-quicksort" rel="nofollow">http:&#x2F;&#x2F;fsharpforfunandprofit.com&#x2F;posts&#x2F;fvsc-quicksort</a>
badman_ting大约 11 年前
<i>Yes, I know this is not a &quot;true&quot;, in-place quicksort. But those are performance considerations that I don&#x27;t intend to expose here.</i><p>Beware, beware.