(defun memoize (fn)
(let ((cache (make-hash-table :test #'equal)))
#'(lambda (&rest args)
(multiple-value-bind (val win) (gethash args cache)
(if win val
(setf (gethash args cache) (apply fn args)))))))
And here is a prettier (use of TryGetValue), C# 3.0 (Func types, local variable type inference) version: static Func<TParam, TReturn> Memoize<TParam, TReturn>(Func<TParam, TReturn> func)
{
var memoDict = new Dictionary<TParam, TReturn>();
return arg =>
{
TReturn result;
if (!memoDict.TryGetValue(arg, out result))
{
result = func(arg);
memoDict[arg] = result;
}
return result;
};
}
The C# version is clearly longer, but less dense. Most of the noise in the C# version comes from the verbose type declarations. Almost anyone could read the C# code, provided they know that => means lambda and at least heard of a closure. That is not true of the Common Lisp version. As with everything in engineering, it is a balance. Lisp is absurdly powerful and C# is absurdly clear to read. As a programmer who does a lot of maintainence programming at work, I highly appreciate that attribute of the language. (defun memoize (fn)
(let ((cache (make-hash-table :test #'equal)))
(lambda (&rest args)
(multiple-value-bind (val win)
(gethash args cache)
(if win
val
(setf (gethash args cache) (apply fn args)))))))
I'm a Lisp programmer and the C# arglist is already hard to parse with lots of noise - for me.Second I don't look for { or ( but for the block indentation, so a { on a single line does not give me enough visual clues.
The 'return arg =>' form is not obvious to me. What does it do? Where dies TReturn come from, TParam?
Why is there }; and } ?
We could argue for days about these sort of issues, and there are passionate ideas on either side of the fence.
function
{
code;
code;
}
In that form, you have the beginning { and ending } for explicitly define the beginning and ending of the code block, and all of the code inside is also indented for good measure. Maybe it's redundant, but I don't see how it's wildly different from: function
code;
code;
> Why is there }; and } ?Have you never programmer outside of lisp? I've never programmed in C#, but if 'arg =>' is the beginning of lambda, then it's because the definition of that line is return EXPRESSION;.
And in that case EXPRESSION = 'arg => { code }'. That's where the ';' comes from.
var square = x => x * x;
var square = x => { return x * x; };The typical/recommended/easy/practical solution is to just provide another overload, pack the args into an object array, and then call the original overload; completely ignoring the problem.
Alternatively, you could do some scary things with reflection...
Even so, with the caveat that you have to learn Common Lisp fist, I think the CL version is infinitely more clear and elegant. All that type information is redundant in the C# version, since in this case all you want to say about the types is that you don't care what they are.
multiple-value-bind is among the longest function name in CL, and doing something that python does much better with syntax alone, perhaps Python is the new Lisp.
Aha. WTF? Ok, if I know what the code means, than I can read it. But that's only the case for C# and not for Common Lisp.
So your statement that The C# one looks very long and ugly is entirely subjective.
(defun memoize (fn) (let ((cache (make-hash-table :test #'equal))) #'(lambda (&rest args) (multiple-value-bind (val win) (gethash args cache) (if win val (setf (gethash args cache) (apply fn args)))))))