Examples and Problems

Remember to let recursion do the work for you.


Problem:

Give rules for converting a list of 0's and 1's representing a binary numeral, least-significant digit first, to a number.

fromBinary( [0, 1, 0, 1, 0, 1] ) ==> 42

 

 

 

Basis:

fromBinary([ ]) => 0;

 

Induction rule:

fromBinary( [ LeastSignificantDigit | Digits ] ) => 

              

Problem:

 

 

Give rules for converting a number to a list of 0's and 1's representing a binary numeral, least-significant digit first.

 

toBinary(42) ==> [0, 1, 0, 1, 0, 1]

 

 

 

Here the argument is not a list, so the basis won't be [ ]. Instead it will be the number 0.

First approximation:

Basis:

toBinary(0) => [0];

 

Induction rule:

toBinary(N) => [             |                        ];

 

In the second rule, N can be assumed to be other than 0. Zero would be captured by the first rule.


The rules above don't quite work; they always puts a high-order 0 at the end of the list. This is harmless, but not really desired. To avoid this, we need to handle the argument 0 separately.

 

 

toBinary(0) => [0];

 

toBinary(N) => toBinary1(N);

 

toBinary1(0) => [];

 

toBinary1(N) => [ N % 2| toBinary1(N / 2) ];

Here toBinary is the interface function and toBinary1 is the helper or auxiliary function.


Problem:

 

Give rules for converting a list of 0's and 1's representing a binary numeral, most-significant digit first, to a number.

 

The pedestrian solution is just to use the previous function fromBinary but call it on a reversed argument:

fromBinaryMSD(List) = fromBinary(reverse(List));

 

How can we do it without reversing the list first?


Conversion from Binary, MSD first:

A solution from scratch is tricky. We want to avoid computing powers of 2 explicitly. We use an auxiliary which returns a list of two arguments, a conversion in progress and a power of 2.

This is a good example of using equational guards:

 

from_binary_msbf(Bits) =

  [Number, Power] = from_binary_msbf1(Bits),

  Number;

 

from_binary_msbf1([]) => [0, 1];

 

from_binary_msbf1([Bit | Bits]) =>

  [Number, Power] = from_binary_msbf1(Bits),

  [Bit*Power+Number, Power*2];

 

from_binary_msbf1 returns the number and the next higher power of 2

 


Problem:

 

Give rules for converting a number to a list of 0's and 1's representing a binary numeral, most-significant digit first.

 

As before, the pedestrian solution is just to use the previous function toBinary but reverse the result:

toBinaryMSD(Number) = reverse(toBinary(Number));

 

How can we do it without reversing the list after?

 

 


 

We use the idea of an accumulator, an argument which accumulates a step at a time the value to be ultimately returned.

 

The interface function handles the case of argument 0:

 

to_binary_msbf(0) => [0];

 

to_binary_msbf(N) => to_binary_msbf(N, []);

 

 

 

to_binary_msbf(0, Acc) => Acc;

 

to_binary_msbf(N, Acc) => to_binary_msbf(N/2, [N%2 | Acc]);

 

The accumulator in the second function builds up the ultimate result inside-out, starting with the least-significant digit:

 

 


Problem:

Construct function pick in the programming language of your choice.

pick(Goal, Items)

where Goal is a non-negative integer and Items is a sequence of positive integers, yields a pair:

The first element of the result pair is the sum of the elements in the second element, as defined next.

The second element of the pair is a set of integers in Items which has a sum (there many be more than one) that is closest to Goal without exceeding it, where each item can be used at most once.

For example:

pick(4,  [1, 8, 3, 5]) ==> [4, [1, 3]]

 

pick(10, [1, 3, 3, 5]) ==> [9, [1, 3, 5]]  

 

pick(0,  [1, 4, 3, 5]) ==> [0, []]

 

In the first example, 1+3 == 4 exactly. In the second, 1+8 == 9, the closest we can come to 10 with the numbers given. In the third case, choosing the empty list sums to 0 exactly.

 


What solution paradigm should be used?

 

 

 


 

pick(Goal, []) => [0, []];		// empty list can only meet goal 0

 

pick(Goal, [Item | Items]) =>

  Item > Goal ? pick(Goal, Items)	// Item too large to use

 

  : [Sum1, Items1] = pick(Goal-Item, Items),	// Try with Item

    [Sum2, Items2] = pick(Goal, Items),		// Try without Item

 

    Sum1 + Item > Sum2 ? 				// Which is closer?

 

      [Sum1+Item, [Item | Items1]] 			// Using item

 

    : [Sum2, Items2];					// Not using item

 

To Next Slide To Previous Slide To Contents