Here are 5 points to think about, hopefully they’ll trigger an ‘aha’ moment.
1. Leveraging the ‘Closure’ concept in programming
I am not talking about the ‘closure’ concept from programming (the one that involves free variables). This refers to the ‘closure‘ concept from mathematics and the power it brings to programming languages.
Mathematically, a set is closed under an operation if carrying out that operation on elements of the set will always give a result that is also in the set. For example, addition of positive integers is closed under the set of positive integers (subtraction is not: 1 – 3 = -2; which is not positive). Thus you can infinitely add positive numbers without worrying about ending up with non-positive numbers.
Lets take this to programming: closed operations allow chaining because the result will be always be a valid value for further operations. Does this strike a bell? Think fluent programming, think jQuery chains. This simple concept allows very complex actions by leveraging simple data and procedures.
2. Simple it might seem, difficult it might be
I initially thought Ex 2.18 would be dead easy; it was simple: reverse a scheme list. I realized my folly after spending the better part of an hour battling the task. Unlike pointer-based lists in C-like languages, the Scheme list is a chain of pairs, each pair points to another pair and so on. This negated walking down the list and reversing pointer directions.
Recursion was tricky since getting the second half of a list pair (i.e. cdr list) brought the entire sublist instead of just the element! The reversal task involved the following restrictions:
- No variables allowed
- Only the top element of a sublist can be retrieved while walking down the list
- The procedure should recursively construct a new list simultaneously
- Retrieval of elements at arbitrary positions is difficult; although possible, the absence of variables makes this useless.
My first approach worked but created a wrong data structure; Alhamdulillaah I eventually got the right solution. An elegant solution I found online solved the reversal problem simply – reversing a list is equivalent to appending the first element of the list to the reversal of the remaining elements.
3. Languages influence thinking and problem modelling
Languages play a big role in the way we think and write code, most of us see programming tasks through an ‘imperative’ pair of glasses. Alhamdulillah the SICP book is giving me a new pair of glasses. Don’t we all need a new pair of glasses every now and then?
Imagine a simple task: calculate the sum of the squares of the odd numbers in an array. The snippets below show both the imperative and functional styles of solving the problem.
function isOdd(num) {
return (num %2 ) === 1;
}
function square(num) {
return num * num;
}
//imperative style
function sumOddSq(arr) {
var sumOddSq = 0;
for(var i=0, l=arr.length; i < l; i++) {
if(isOdd(arr[i])) {
sumOddSq += square(arr[i]);
}
}
return sumOddSq;
}
//functional style
function sumOddSq2(arr) {
return arr.filter(isOdd)
.map(square)
.reduce(function (val, acc) {
return val + acc;
}, 0);
}
sumOddSq([1,2,3]); //10
sumOddSq2([1,2,3]); //10
The imperative approach involves walking through the array, checking for odd numbers, squaring those odd numbers and updating the sum as you go along. Using a functional programming approach, the task involves filtering the sequence for odd numbers, mapping the filtered numbers to their squares and then reducing these squares to a sum.
Functional programming relies heavily on the map, reduce and filter concepts and most problems can be solved based on these building blocks. For tasks involving non-list-like structures, converters can be used to create sequences and also generate structures from list results.
Did you notice? sumOddSq2 chains array operations; the map, reduce and filter operations can be said to be ‘closed’ over the set of arrays. Thus the result of any of these operations is always an array and can be reused immediately.
4. Software engineering is still engineering
Even though software engineers manipulate bits and not ‘real’ hardware; general engineering concepts still apply. There are a couple of interesting similarities; for example in electronics engineering, the diode can be viewed as an if statement while filters can be viewed as ‘filter‘ logic. The electronics engineer uses a filter, the software engineer uses a filter function. Both combine them to build even more complex abstractions.
Another example is the computer system and the various dependent layers; a simplified complexity order is shown below:
Transistors -> Logic gates -> Circuits -> Processors -> Computers -> Distributed Systems
The same applies to software engineering, an application can be seen as:
Values -> Methods -> Classes -> Models -> Subsystems -> Modules -> Applications
We all start from something simple and progressively add complexity, each layer being shielded from the layer below it. A computer engineer can swap processors without breaking a computer; so should a software engineer be able to swap out layers in his code without bringing down the application.
5. Recursion
The 8 queens problem is one that a lot of people are familiar with and as you guessed, there was an exercise on that. The solution was quite simple – recursively build the board starting out with one queen. As the board grows, filter out positions that have queens attacking each other, finally return the list of all positions that have safe queens.
The functional-programming styled solution involved enumerating all positions, creating a safe? function to determine a valid board, passing this function to the filter and then outputting the results. It is great to see how functions can be composed and how the basic functional programming support for map, reduce and filter enable even higher levels of complexity.
I must admit I struggled a lot with this problem as it was difficult to understand and debug – walking up the call stack did expose some of its intricacies but it’s amazing how simple and how yet powerful the results of combining these simple concepts.
Conclusion
And that’s about it. Next is section 2.3 with about 20 questions; I skipped about 2 or 3 questions in 2.2 since I didn’t really feel they were worth solving. I hope I can get 2.3 done by mid January insha Allaah and create another post then!
Related Posts
Here are a couple more of my SICP-themed posts
1. SICP Section 2.1 : Thoughts
2. 5 things I have gained from SICP: Section 1.2
3. SICP Section 1.3 : Thoughts and Concepts
“Thus you can infinitely add positive numbers without worrying about ending up with non-positive numbers”
Yet I thought the sum of all natural numbers was in fact a non-positive number (-1/12) !?!!
Loved your article on list manipulation by the way.
LikeLike
Thank you Gary! Glad that you enjoyed it!!
I looked up the -1/12 concept and found out it was just posited as a representation for that value. The sum is divergent and will get larger over time.
Great concept all the same and thank you!
LikeLike