Can a typical Lisp dialect solve problems using the bottom-up “dynamic programming” approach?
(Please note: I’m not talking about “memoization” which, as far as I understand, is trivial using any Lisp dialect. I’m really talking about bottom-up Dynamic Programming, where you build, for an example, your array bottom up and then use the elements you just introduced to compute the next ones.)
For example, using dynamic programming, the “0-1 knapsack” problem can be solved in pseudo-polynomial time for inputs on which any other method would fail.
An imperative (incomplete) solution is:
for (int k = 1; k <= a.length; k++) {
for (int y = 1; y <= b; y++) {
if (y < a[k-1]) {
knap[k][y-1] = knap[k-1][y-1];
} else {
if (y > a[k-1]) {
knap[k][y-1] = Math.max(knap[k-1][y-1], knap[k-1][y-1-a[k-1]] + c[k-1]);
} else {
knap[k][y-1] = Math.max(knap[k-1][y-1], c[k-1]);
}
}
Is such a thing possible to do in the various Lisp dialects? If no, why not?
Of course this is possible. The only things you need are arrays, integers and a loop construct. E.g., in Scheme, your algorithm can be transcribed using vectors. The main problem is that it becomes hard to read, since
knap[k-1][y-1]becomes(vector-ref (vector-ref knap (- k 1)) (- y 1))andbecomes
(or a hairy trick with moduli), while memoized recursions just remain readable.
Speaking from experience, I recommend you stick to the memoization when programming in Lisp and similar languages. If you use hash tables, the expected asymptotic time and space complexity are the same for memoization and DP.