Due on: Mar 6
There is a limit on how large an integer the ML programming language can handle.
We want to augment the ML language with functions Add and Multiply that can handle huge nonnegative integers. Since we cannot use the regular ML integers (as the above example shows), we will represent our integers with strings. An example for the use of Add and Multiply is given as follows:cherokee[67] sml Standard ML of New Jersey, Version 110.0.3, January 30, 1998 - val x=123456789; val x = 123456789 : int - val x=1234567890; stdIn:9.6-9.16 Error: int constant too large
- Add("123456789123456789","987654321987654321");
val it = "1111111111111111110" : string
- Multiply("123456789123456789","987654321987654321");
val it = "121932631356500531347203169112635269" : string
To make the problem easier, we assume that the huge integers that we need
to handle are nonnegative. That is, Add and Multiply
both take arguments that are strings of digits. Negative signs are not
allowed in the arguments given to Add and Multiply.
We will implement Add and Multiply in a number of steps.
Consider an example of a huge integer "123456789" which we represent as a string. In order that we can process the integer, we need to represent it as a list of digits. The predefined ML function explode will turn a given string into a list of characters. That is,
explode("123456789")
returns the character list
with the head of the list being "1".head | \|/ [#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9" ]
Correspondingly, the function implode takes a character list and returns the corresponding string by concatenating the characters in the given list. That is, implode is the converse of explode.
1. Write a function to_int_list( cl: char list ) such that given a list of characters of digits, say
, the function returns the corresponding list of integers, likehead | \|/ [#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9" ]
Hint: Given a character #"7", the expression ord(#"7") - ord(#"0") returns the integer value 7. The same is true for other decimal digit characters.head | \|/ [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
A template of the function is given as follows:
fun to_int_list( cl: char list ) = if cl = nil then nil else (* to be completed *) :: (* to be completed *) ;
We also need a function that does the converse of to_int_list.
2. Write a function to_char_list( il: int list ) such that given a list of digits, say
, the function returns the corresponding list of chars, likehead | \|/ [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Hint: Given an integer 7, the expression chr(7 + ord(#"0")) returns the char value 7. The same is true for other digits.head | \|/ [#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9" ]
A template of the function is given as follows:
fun to_char_list( il: int list ) = if il = nil then nil else (* to be completed *) :: (* to be completed *) ;
Suppose we have two integers "123456789" and "234567890" that we want to add together. By applying to_int_list to the two integers, we have two lists of integers
andhead | \|/ [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
with the heads of the two lists being 1 and 2 respectively.head | \|/ [ 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
However, when we do math, we do not perform addition starting from the higher-ordered digits. We start adding from the lower-ordered digits, and move the addition process towards the higher-ordered digits. That is, we add 9 and 0 to give 9, next add 8 and 9 to give 7 with the carry 1, next add 7 and 8 with carry 1 to give 6 with the carry 1, and so on.
Thus, it is more appropriate if we can reverse the list so that the head of the list is at the lowest-ordered digit:
andhead | \|/ [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Note that in the normal way we write lists, these would be [9,8,7,6,5,4,3,2,1] and [0,9,8,7,6,5,4,3,2].head | \|/ [ 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
3. Write a function that returns the reverse of a given list. Such a function is given in class and in the textbook. A template for the function is given below:
HINT: look at the "@" operator on page 38 in your ML book.fun reverse(L) = if L = nil then nil else (* to be completed *) ;
4. Write a function string_to_list( numstr: string ) that takes a string of digits and returns the reverse of the list of digits. For example, string_to_list( "923074" ) gives
which is equivalent to [ 4, 7, 0, 3, 2, 9 ] in the usual list notation. Hint: your function needs to make use of reverse, to_int_list, and explode.head | \|/ [ 9, 2, 3, 0, 7, 4 ]
5. Write a function list_to_string( il: int list ) which is the converse of string_to_list( a: string ). For example, list_to_string( [4, 7, 0, 3, 2, 9 ] ) gives the answer "923074". That is, list_to_string( string_to_list( "923074" ) ) gives "923074". Hint: your function needs to make use of reverse, to_char_list, and implode.
Consider the number "703", the corresponding list representation is as follows:
Let the list be denoted by L. We can apply functions hd and tl to the list. We have hd(L) = 3 and tl(L) ishead | \|/ [ 7, 0, 3 ]
Let the list tl(L) be denoted by L1. We have hd(L1) = 0 and tl(L1) ishead | \|/ [ 7, 0 ]
Let the list tl(L1) be denoted by L2. We have hd(L2) = 7 and tl(L2) is nil which we denote by L3.head | \|/ [ 7 ]
If we attempt to compute hd(L3) and tl(L3), ML responds as follows:
But this may not be desirable. Since we are dealing with a number 703, conceptually we could think of the number as being padded with an infinite number of 0's to its left. That is, 703 can be considered as- hd(L3); uncaught exception Empty raised at: boot/list.sml:36.38-36.43 - tl(L3); uncaught exception Empty raised at: boot/list.sml:37.38-37.43
In a sense, the null list nil corresponds to the conceptual infinite list ....00000. That is, for this problem it is more appropriate if hd(nil) returns 0 instead of causing an exception. Similarly, we want tl(nil) to return again nil......0000000703
6. Write a function Hd( L: int list ) such that it returns 0 if L is nil, and returns the head element if L is not empty.
7. Write a function Tl( L: int list ) such that it returns nil if L is nil, and returns tl(L) if L is not empty.
We want to write a function add( a: int list, b: int list ) that can add two int lists of digits. We assume that the two lists are ordered in a such a way that the lower-ordered digits are located in the front of the list. That is, the lowest-ordered digit can be accessed by applying the function Hd to the list. Also, the function add returns a list of digits such that the lowest-ordered digit is located at the head of the list.
To implement add( a: int list, b: int list ), we introduce another function add_carry( a: int list, b: int list, carry: int ) where carry is either 0 or 1. The function add_carry adds two lists of decimal digits a and b together with the carry. For example, if a is
and b ishead | \|/ [ 4, 3, 7 ]
Then the result of add_carry( a, b, 1 ) ishead | \|/ [ 3, 8, 3, 7 ]
and the result of add_carry( a, b, 0 ) ishead | \|/ [ 4, 2, 7, 5 ]
head | \|/ [ 4, 2, 7, 4 ]
8. Write a function add_carry( a: int list, b: int list, carry: int ) as defined above. A template of the function is given below:
Hint: To complete the function, you need to make use of div and mod operations, and the functions Hd and Tl.fun add_carry( a: int list, b: int list, carry: int ) = if a=nil andalso b=nil andalso carry=0 then nil else (* to be completed *) :: (* to be completed *);
9. Implement the function add( a: int list, b: int list) in terms of add_carry. Note that add is not a recursive function (add_carry does all the work).
10. Now, write a function Add( a: string, b: string ) that behaves as below:
- Add("123456789123456789","987654321987654321");
val it = "1111111111111111110" : string
- Add("5240006432","81238888");
val it = "5321245320" : string
Hint: Your implementation of Add has to make use of the functions
add, string_to_list, list_to_string. Test your function to make
sure that it gives the above results.
As an intermediate step, we need to introduce another function mult_carry( a: int list, d: int, carry: int ) which multiplies a list of digits by an single digit d and add the result with the carry which could be any single decimal digit. For example, let a be
, d be 8 and carry be 7. Then the result of mult_carry ishead | \|/ [ 5, 0, 7, 3, 2 ]
That is, 50732*8 + 7 = 405863.head | \|/ [ 4, 0, 5, 8, 6, 3 ]
11. Write the function mult_carry( a: int list, d: int, carry: int ) as defined above. A template of the function is given below:
Hint: To complete the function, you need to make use of div and mod operations, and the functions Hd and Tl. Note that the logic is similar to that of add_carry.fun mult_carry( a:int list, d: int, carry: int ) = if (a=nil orelse d=0) andalso carry=0 then nil else (* to be completed *) :: (* to be completed *);
12. Write the function multiply( a: int list, b: int list ) which multiplies two given list of digits. For example, if a is
and b ishead | \|/ [ 1, 2, 2 ]
, then the result of multiply ishead | \|/ [ 1, 2 ]
. That is, 122 * 12 = 1464. A template for the function is given below:head | \|/ [ 1, 4, 6, 4 ]
Hint: How do we multiply two numbers say a=12345 and b=1232? We can multiply a by the lowest-ordered digit of b which is 2 to give 24690. Next, we multiply a by 123 (which is obtained by taking out the last digit from b) to give an intermediate result which we first pad it with a 0 before adding with 24690 to give the final result. To complete the details for the function multiply, you need to make use of the operator :: and the functions add, mult_carry, hd, tl.fun multiply( a: int list, b: int list ) = if a=nil orelse b=nil then nil else (* to be completed *);
13. Now that the function multiply( a: int list, b: int list ) has been implemented, write the function Multiply( a: string, b: string ) that behaves as below:
- Multiply("123456789123456789","987654321987654321");
val it = "121932631356500531347203169112635269" : string
- Multiply("34567","123456789");
val it = "4267530825363" : string
- Multiply("234567891","98765");
val it = "23167097754615" : string
Test your function Multiply to see that it gives the above result.