4 ms·
Some thoughts from an ex-CL'er that has dabbled in Clojure for 3-4 years: Clojure's combination of declarative idioms and highly functional idioms (to a greate
by codewright 14y ago
Some thoughts from an ex-CL'er that has dabbled in Clojure for 3-4 years:
Clojure's combination of declarative idioms and highly functional idioms (to a greater extent than CL) lead to it being able to by default express certain things in a more terse fashion.
http://rosettacode.org/wiki/Factors_of_an_integer http://rosettacode.org/wiki/Factors_of_an_integer
Compare Common Lisp and Clojure here.
Common Lisp:
(defun factors (n &aux (lows '()) (highs '()))
(do ((limit (isqrt n)) (factor 1 (1+ factor)))
((= factor limit)
(when (= n (* limit limit))
(push limit highs))
(nreconc lows highs))
(multiple-value-bind (quotient remainder) (floor n factor)
(when (zerop remainder)
(push factor lows)
(push quotient highs)))))
Common Lisp in this case is relying heavily on mutation with push. It's not that much better than a typical loop in an iterative language like Python.
Clojure solutions:
; Trivial version
(defn factors [n]
(filter #(zero? (rem n %)) (range 1 (inc n))))
Initial version just does a filter and a lambda across the sequence produced by range. inc so that it includes the final result. Pretty functional.
; Less trivial
(defn factors [n]
(into (sorted-set)
(mapcat (fn [x] [x (/ n x)])
(filter #(zero? (rem n %)) (range 1 (inc (sqrt n)))) )))
Now we're getting a little more clever, relying on mapcat, filter, a lambda, and range.
; Now with a for-comprehension.
(defn factors [n]
(into (sorted-set)
(reduce concat
(for [x (range 1 (inc (sqrt n))) :when (zero? (rem n x))]
[x (/ n x)]))))
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/into http://clojure.github.com/clojure/clojure.core-api.html#cloj...
Take a look at the other things that work across the seq'able data structures. Pretty powerful base set of idioms.
Okay, but lets provide a second opinion on these idioms. What about Haskell?
import HFM.Primes(primePowerFactors)
import Data.List
factors = map product.
mapM (uncurry((. enumFromTo 0) . map .(^) )) . primePowerFactors
Using a primes module for finding prime factors. Okay, isn't that kinda cheating Rosetta Code though? I was kinda hoping to find the equivalent of the Clojure code.
And Mathematica?
Factorize[n_Integer] := Divisors[n]
FFS.
I give up. These are dumb comparisons. Don't listen to this Wolfram post. At least the Common Lisp/Clojure code wasn't pointless.
- jamesjporter 14y agoPerhaps what we can take away from this is that Mathmatica (at least in this case, and I would imagine many others) is more terse that other languages because the base language includes features such as factorization that require libraries in other languages. This is obviously beneficial in many cases, but I agree with you that it is disingenuous to suggest that Mathematica's terseness is primarily due to well-designed syntax/semantics as opposed to feature-richness.
- codewright 14y agoI agree with you there. Another thought from my experiences: The only way I've seen to scale up the "expressiveness" in a programming language in a way that's generally applicable is to make it more declarative. I have mixed feelings about this. Two primary ways of doing so are functional idioms for "iteration" and concatenative programming. Things like juxt, mapcat, et al have their equivalents in languages like J, K, Factor, etc. The only way to elevate yourself above loops and manually assigning intermediates is to flow the data through high-level descriptions of what you want. Whether that takes the form of a concatenative or functional langauge depends on the coder. OOP is more about scaling up procedural idioms so that state management and code reuse aren't a total Vietnam. They aren't really more "expressive" with the exception of some of the idioms Smalltalk tossed about. All that's left otherwise is "ecosystem leverage" such as Mathematica exhibits here. That's not really about the semantics of the programming language, but it's still worth noting if the leverage is relevant to the problem you're solving.
- carlob 14y agoThis. As I said before: there is a lot that is a library elsewhere that's built-in in Mathematica. And that's why I think Mathematica is very good at rapid prototyping.
- lispm 14y agoThe Common Lisp code is carefully crafted to minimize the number of divisions.
- gjm11 14y agoCommon Lisp code basically equivalent to that first "trivial" Clojure implementation (WARNING: untested, may consist entirely of bugs): (defun factors (n) (loop for r upfrom 1 to n when (zerop (remainder n r)) collect r)) Clojure doesn't have much advantage in conciseness over CL here. It's just a matter of what approach the contributor chose to use.