# Asteroid in Action¶

This document was inspired by Andrew Shitov’s excellent book Using Raku: 100 Programming Challenges Solved with the Brand-New Raku Programming Language. Here we use Asteroid to solve these programming challenges.

## Section: Using Strings¶

### Challenge: Hello, World!¶

> Print ‘Hello, World!’

The canonical Hello, World! program. The easiest way to write this in Asteroid is,

load system io.

io @println "Hello, World!".


Output:

Hello, World!


### Challenge: Greet a person¶

> Ask a user for their name and greet them by printing ‘Hello, <Name>!’

Here is our first solution using a separate function for each of the steps,

load system io.

io @print ("Enter your name: ").
let name = io @input().
io @print ("Hello, "+name+"!").


Letting the function input do the prompting,

load system io.

let name = io @input("Enter your name: ").
io @println ("Hello, "+name+"!").


Doing everything in one step,

load system io.

io @println ("Hello, "+io @input("Enter your name: ")+"!").


### Challenge: String length¶

> Print the length of a string.

In order to print the length of a string we can use the function len available in the util module,

load system io.

io @println (len("Hello!")).


Output:

6


We can also use the string member function length in order to compute the length of the string,

load system io.

io @println ("Hello!" @length()).


Output:

6


### Challenge: Unique digits¶

> Print unique digits from a given integer number.

In order to accomplish this we take advantage of the string explode function and the sort function on lists. Finally we use the reduce function to map a list with repeated digits to a list with unique digits,

load system io.

function unique with (x,y) do
if not (x @member(y)) do
return x @append(y).
else do
return x.
end
end

let digits = "332211" @explode()
@sort()
@reduce(unique,[]).
io @println digits.
assert(digits == ["1","2","3"]).


Output:

[1,2,3]


Probably the most noteworthy characteric about this program is the reduce function. The reduce function applies a binary function to a list. The first argument of the binary function acts like an accumulator, and the second argument gets instantiated with the elements of the list to be processed. In our function unique, the variable x is the accumulator with an initial value of []. The function tests whether the element y is in the list. If it is not, then it adds it to the list. Otherwise, it just returns the accumulator unchanged.

## Section: Modifying String Data¶

### Challenge: Reverse a string¶

> Print a string in the reversed order from right to left.

We use the explode function to turn a string into a list of characters. Then, we reverse the list and turn it back into a string using the join function,

load system io.

let str = "Hello, World!" @explode()
@reverse()
@join("").
io @println str.
assert(str == "!dlroW ,olleH").


Output:

!dlroW ,olleH


### Challenge: Removing blanks from a string¶

> Remove leading, trailing, and double spaces from a given string.

load system io.
let str = "   Hello  ,   World    !   " @trim()
@replace("  ","").
io @println str.
assert(str == "Hello, World!").


Output:

Hello, World!


### Challenge: Camel case¶

> Create a camel-case identifier from a given phrase.

In this task, we will form the CamelCase variable for names from a given phrase. Names created in this style are built of several words, each of which starts with a capital letter.

load system io.

function title with w do
let letter_list = w @tolower()
@explode().
let first_letter = letter_list @0
@toupper().
if letter_list @length() > 1 do
let title_case = ([first_letter] + letter_list @[1 to letter_list@length()-1]) @join("").
else
let title_case = first_letter.
end
return title_case.
end

let str = "once upon a time".
let camel_str = str @split()
@map(title)
@join("").
io @println camel_str.
assert(camel_str == "OnceUponATime").


Output:

OnceUponATime


### Challenge: Incrementing filenames¶

> Generate a list of filenames like file1.txt, file2.txt, etc.

load system io.

let root = "file".
let ext = ".txt".

for i in 1 to 5 do
io @println (root+i+ext).
end


Output:

file1.txt
file2.txt
file3.txt
file4.txt
file5.txt


> Generate a random string that can be used as a password.

In our solution we take advantage of Asteroid’s Pick object. The Pick object maintains a list of items that we can randomly select from using the pick member function. As input to the Pick object, we compute a bunch of lists of characters that are useful for password construction. The function achar converts a decimal ASCII code to a single character string.

load system io.

random @seed(42).

-- make up lists of symbols useful for password construction
let int_list = [0 to 9] @map(type @tostring).
let lc_list = [97 to 122] @map(util @achar). -- lower case characters
let uc_list = [65 to 90] @map(util @achar). --upper case characters

### Challenge: Standard deviation¶

> For the given data, calculate the standard deviation value (sigma).

Standard deviation is a statistical term that shows how compact data distribution is. The formula is the following:

$\sigma = \sqrt\frac{1}{n-1}\sum_i(\bar{x} - x_i)^2$

where $$n$$ is the number of elements in the array $$x$$; $$\bar{x}$$ is its average value (Challenge: Average on an array).

load system io.

let values = [727.7, 1086.5, 1091.0, 1361.3, 1490.5, 1956.1].

let avg = values @reduce(lambda with (x,y) do return x+y) / values @length().
let diff_sq = values @map(lambda with x do return math @pow(x-avg,2)).
let numerator = diff_sq @reduce(lambda with (x,y) do return x+y).
let denominator = values @length() -1.
let sigma = math @sqrt(numerator/denominator).
io @println sigma.

assert (sigma == 420.96248961952256)


Output:

420.96248961952256


### Challenge: Polar coordinates¶

> Convert the Cartesian coordinates to polar and backward.

Polar coordinates are a convenient way of representing points on a surface with the two values: distance from the centre of coordinates, and the angle between the vector and the pole axis. The conversion formulae between the Cartesian and polar systems, which is valid for positive x and y, are the following:

x = r cos(psi)
y = r sin(psi)
r = sqrt(x^2 + y^2)
psi = arctan(x/y)


These expressions can be implemented as-is in the code:

load system io.

-- define common math functions locally so the
-- formulas are easy to read
let cos = math @cos.
let sin = math @sin.
let sqrt = math @sqrt.
let pow = math @pow.
let atan = math @atan.

function polar_to_cartesian with (r,psi) do
-- return a tuple: (x,y)
return (r*cos(psi),r*sin(psi)).
end

function cartesian_to_polar with (x,y) do
-- return a tuple: (r,psi)
return (sqrt(pow(x,2)+pow(y,2)),atan(y/x)).
end

let (r,psi) = cartesian_to_polar(1,2).
let (x,y) = polar_to_cartesian(r,psi).

io @println (x,y).

-- show that the recovered coordinates are the same
-- we started with
assert (math @isclose(1,x,0.0001) and math @isclose(2,y,0.0001)).


Output:

(1.0000000000000002,2.0)


For the negative x and y, the Cartesian-to-polar conversion is a bit more complicated. Depending on the quadrant of the point, the psi value is bigger or smaller than pi. When x is zero, it is either -pi/2 or pi/2. All these variants can be implemented by using with clauses and conditional matching, as demonstrated below:

load system io.

-- define common math functions locally so the
-- formulas are easy to read
let cos = math @cos.
let sin = math @sin.
let sqrt = math @sqrt.
let pow = math @pow.
let atan = math @atan.
let pi = math @pi.
let toreal = type @toreal.

function polar_to_cartesian with (r,psi) do
-- return a tuple: (x,y)
return (r*cos(psi),r*sin(psi)).
end

function cartesian_to_polar with (x,y) do
return (sqrt(pow(x,2)+pow(y,2)),cartesian_to_psi(x,y)).
end

function cartesian_to_psi
with (x,y) if x > 0  do
return atan(toreal(y)/x).
with (x,y) if x < 0 and y >= 0 do
return atan(toreal(y)/x)+pi.
with (x,y) if x < 0 and y < 0 do
return atan(toreal(y)/x)-pi.
with (x,y) if x == 0 and y > 0 do
return pi/2.
with (x,y) if x == 0 and y < 0 do
return -pi/2.
with (x,y) if x == 0 and y == 0 do
return none.
end

let (r,psi) = cartesian_to_polar(-3,5).
let (x,y) = polar_to_cartesian(r,psi).

io @println (x,y).

-- show that the recovered coordinates are the same
-- we started with
assert (math @isclose(-3,x,0.0001) and math @isclose(5,y,0.0001)).


Output:

(-2.999999999999999,5.000000000000001)


### Challenge: Monte Carlo method¶

> Calculate the area of a circle of radius 1 using the Monte Carlo method.

The Monte Carlo method is a statistical method of calculating data whose formula is not known. The idea is to generate a big number of random numbers and see how many of them satisfy the condition.

To calculate the area of a circle with a radius of 1, pairs of random numbers between −1 and 1 are generated. These pairs represent the points in the square in the center of coordinates with sides of length 2. The area of the square is thus 4. If the distance between the random point and the center of the square is less than 1, then this point is located inside the circle of that radius. Counting the number of points that landed inside the circle and the number of points outside the circle gives the approximate value of the area of the circle, as soon as the area of the square is known. Here is the program.

load system io.

let sqrt = math @sqrt.
let pow = math @pow.
let randint = random @randint.

random @seed(42).

let inside = 0.
let n = 10000.
for _ in 1 to n do
let point = (randint(-1.0,1.0),randint(-1.0,1.0)).
if sqrt(pow(point@0,2)+pow(point@1,2)) <= 1.0 do
let inside = inside+1.
end
end
let area = 4.0 * inside / n.
io @println area.

assert (area == 3.1392).


Output:

3.1392


### Challenge: Guess the number¶

> Write a program that generates a random integer number between 0 and 10, asks the user to guess it, and says if the entered value is too small or too big.

First, a random number needs to be generated. Then the program must ask for the initial guess and enter the loop, which compares the guess with the generated number.

load system io.

random @seed(42).

let n = random @randint(0,10).
let guess = type @tointeger(io @input("Guess my number between 0 and 10: ")).
while guess =/= n do
if guess < n do
io @println "Too small.".
elif guess > n  do
io @println "Too big.".
end
let guess = type @tointeger(io @input("Try again: ")).
end
io @println "Yes, this is it!".


### Challenge: Binary to integer¶

> Convert a binary number to a decimal integer.

In Asteroid this is straightforward using the built-in tointeger function, passing it a string representation of the binary number and the base.

load system io.

let bin = "101101".
let int = type @tointeger(bin,2).
io @println int.

assert (int == 45).


Output:

45


### Challenge: Integer as binary, octal, and hex¶

> Print a given integer number in the binary, octal, and hexadecimal representations.

In Asteroid this is easily done with the tobase function.

load system io.

let tobase = type @tobase.
let tointeger = type @tointeger.

let val = 42.

io @println (tobase(val,2)).  -- bin
io @println (tobase(val,8)).  -- oct
io @println (tobase(val,16)). -- hex

-- make sure that conversions are correct in both directions
assert (tointeger(tobase(val,2),2) == val).
assert (tointeger(tobase(val,8),8) == val).
assert (tointeger(tobase(val,16),16) == val).


Output:

101010
52
2A


### Challenge: Sum of digits¶

> Calculate the sum of digits of a given number.

Pretty straightforward using string and list manipulation.

load system io.

let number = 139487854.

let s = type @tostring number @explode()
@map(type @tointeger)
@reduce(lambda with (x,y) do return x+y).
io @println s.

assert (s == 49).


Output:

49


### Challenge: Bit counter¶

> Count the number of bits set to 1 in a binary representation of a positive integer number.

If we remove all the zeros from a binary number, then we are left with only 1 characters which we can then count.

load system io.

let bits = "1010101" @replace("0","")
@length().
io @println bits.

assert (bits == 4).


Output:

4


### Challenge: Compose the largest number¶

> Given the list of integers, compose the largest possible number by concatenating them.

The easiest way to achieve that is to treat the numbers as strings, sort them alphabetically in descending order, concatenate the pieces to a single string, and get the resulting integer.

load system io.

let a = type @tointeger([67, 8, 1, 5, 45] @map(type @tostring) @sort(true) @join("")).
io @println a.

assert (a == 8675451).


Output:

8675451


### Challenge: Convert to Roman numerals¶

> Convert an integer number to a Roman numerals string.

Roman numbers are not a direct translation of the decimal system. In this task, we assume that the number is not more than 3999, which is the maximum a regular Roman number can reach.

Let’s use the algorithm that keeps the table of pre-calculated sequences of Roman letters. This is so that we don’t have to check when III becomes IV, or when another I appears after V, etc.

In the program below, there are four such sequences: for thousands, hundreds, tens, and ones. The program iterates over the digits of the number in the decimal representation and chooses one of the values from the array of lists stored in the roman_hash table.

load system io.

let roman_hash = hash @hash().
roman_hash @insert(1000,["","M","MM","MMM"]).
roman_hash @insert(100,["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"]).
roman_hash @insert(10,["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"]).
roman_hash @insert(1,["","I","II","III","IV","V","VI","VII","VIII","IX"]).

let n = 2018.
let p10 = range(type @tostring n @length()) @map(lambda with x do return math @pow(10,x))
@reverse().
let digits = type @tostring n @explode()
@map(type @tointeger).
let z = util @zip(digits, p10).
io @println z.
let roman = "".
for (d,p) in z do
let roman = roman + roman_hash @get(p) @d.
end
io @println roman.

assert (roman == "MMXVIII")


Output:

[(2,1000),(0,100),(1,10),(8,1)]
MMXVIII


### Challenge: Spelling numbers¶

> Write an integer number below one million in words.

Human languages have many inconsistencies, especially in the most frequent constructs. Spelling numbers seems to be a simple task, but due to a number of small differences, the resulting program is quite big.

The program is listed on the next page. Let’s discuss the algorithm first.

Take a number; for example, 987,654. The rules for spelling out the groups of three digits, 987 and 654, are the same. For the first group, the word thousand must be added.

Now, examine a group of three digits. The first digit is the number of hundreds, and it has to be spelled only if it is not zero. If it is not zero, then we spell the digit and add the word hundred.

Now, remove the leftmost digit, and we’ve got two digits left. If the remaining two digits form the number from 1 to 20, then it can be directly converted to the corresponding name. The names for the numbers from 0 to 10 are obviously different. The names for the numbers from 11 to 19 have some commonalities, but is it still easier to directly prepare the names for all of them.

For the larger numbers (21 to 99), there are two cases. If the number is dividable by 10 then a name for 20, 30, 40, etc. is taken. If not, then the name is built of the name of tens and the name for units, joined with a hyphen, such as forty-five.

The zero name appears only in the case when the given number is zero.

load system io.

let mod = math @mod.

let names = ["zero","one","two","three","four","five","six","seven","eight","nine",
"ten","eleven","twelve","thirteen","fourteen","fifteen",
"sixteen","seventeen","eighteen","nineteen","twenty","thirty",
"forty","fifty","sixty","seventy","eighty","ninety"].

function spell_number
with (n:%integer) if n < 20 do
return names @n.
with (n:%integer) if n < 100 do
let r = names @(n / 10 + 18).
let r = r + ("-" + names @(mod(n,10))) if mod(n,10) else "".
return r.
with (n:%integer) if n < 1000 do
return spell_part(n,100,"hundred").
with (n:%integer) if n < 1000000 do
return spell_part(n,1000,"thousand").
end

function spell_part
with (n:%integer,base:%integer,name:%string) do
let r = spell_number(n/base) + " " + name.
return r + " " + spell_number(mod(n,base)) if mod(n,base) else r.
end

io @println (spell_number 15).
io @println (spell_number 75).
io @println (spell_number 987654).
io @println (spell_number 1001).


Output:

fifteen
seventy-five
nine hundred eighty-seven thousand six hundred fifty-four
one thousand one


## Section: Manipulating Lists and Arrays¶

### Challenge: Swap two values¶

> Swap the values of two variables.

In Asteroid, there is no need to use temporary variables to swap the values of two variables. Just use tuples on both sides of the equation:

let (b,a) = (a,b).


Consider the complete program:

load system io.

let (a,b) = (10,20).
let (b,a) = (a,b).
io @println ("a = "+a,"b = "+b).

assert ((a,b) is (20,10)).


Output:

(a = 20,b = 10)


This program prints the swapped values:

(a = 20,b = 10)


This approach also works with elements of an array:

load system io.

let a = [3,5,7,4].
let (a@2,a@3) = (a@3,a@2).
io @println a.

assert (a is [3,5,4,7]).


Output:

[3,5,4,7]


### Challenge: Reverse a list¶

> Print the given list in reverse order.

load system io.

let a = [10, 20, 30, 40, 50].
io @println (a @reverse()).

assert(a == [50,40,30,20,10]).


Output:

[50,40,30,20,10]


### Challenge: Rotate a list¶

> Move all elements of an array N positions to the left or to the right.

Asteroid does not have a built-in rotate function. However, such a function is easily constructed through slicing lists (see vix below).

load system io.

function rotate with (l:%list,i:%integer) do
let n = l @length().
let vix = range n @map(lambda with x do return math @mod(x+i,n)).
return l @vix.
end

let a = [1, 3, 5, 7, 9, 11, 13, 15].
let b = rotate(a,3).
let c = rotate(a,-3).
io @println a.
io @println b.
io @println c.

assert(b == [7,9,11,13,15,1,3,5] and c == [11,13,15,1,3,5,7,9]).


Output:

[1,3,5,7,9,11,13,15]
[7,9,11,13,15,1,3,5]
[11,13,15,1,3,5,7,9]


### Challenge: Randomize an array¶

> Shuffle the elements of an array in random order.

This is easily accomplished with the built-in shuffle.

load system io.

random @seed(42).
let b = [1 to 20] @shuffle().
io @println b.

assert(b == [20,6,15,5,10,14,16,19,7,13,18,11,2,12,3,17,8,9,1,4]).


Output:

[20,6,15,5,10,14,16,19,7,13,18,11,2,12,3,17,8,9,1,4]


### Challenge: Incrementing array elements¶

> Increment each element in an array.

For this we use Asteroid’s vector module, which can handle incrementing a vector with a scalar.

load system io.

let a = [1 to 10].
io @println b.

assert(b == [2,3,4,5,6,7,8,9,10,11]).


Output:

[2,3,4,5,6,7,8,9,10,11]


### Challenge: Adding up two arrays¶

> Take two arrays and create a new one whose elements are the sums of the corresponding items of the initial arrays.

Again, here we take advantage of Asteroid’s vector module. Note that the two vectors have to be of the same length in order to add them together.

load system io.

let a = [10 to 20].
let b = [30 to 40].
io @println c.

assert(c == [40,42,44,46,48,50,52,54,56,58,60]).


Output:

[40,42,44,46,48,50,52,54,56,58,60]


The vector module defines a function called op that allows you to combine two vectors using any arbitrary binary function. Rewriting the above program using op,

load system io.

let a = [10 to 20].
let b = [30 to 40].
let c = vector @op((lambda with (x,y) do return x+y),a,b).
io @println c.

assert(c == [40,42,44,46,48,50,52,54,56,58,60]).


Output:

[40,42,44,46,48,50,52,54,56,58,60]


As we said above, any arbitrary binary function. Consider the relational operator < expressed as a lambda function,

load system io.

random @seed(42).

let a = [1 to 10] @shuffle().
let b = [1 to 10] @shuffle().
let c = vector @op((lambda with (x,y) do return x<y),a,b).
io @println c.

assert(c == [false,true,false,false,false,true,false,false,true,true]).


Output:

[false,true,false,false,false,true,false,false,true,true]


### Challenge: Exclusion of two arrays¶

> From the given two arrays, find the elements of the first array which do not appear in the second one.

Here we use Asteroid’s set module.

load system io.

let a = [1 to 10].
let b = [5 to 15].
let c = set @diff(a,b).
io @println c.

assert(c @sort() == [1,2,3,4]).


Output:

[2,3,1,4]


## Section: Information Retrieval¶

### Challenge: Sum of the elements of an array¶

> Find the sum of the elements of an array of integers.

load system io.

let a = [4, 6, 8, 1, 0, 58, 1, 34, 7, 4, 2].
let s = a @reduce(lambda with (x,y) do return x+y).
io @println s.

assert (s == 125).


Output:

125


If summing up elements that are greater than 10,

load system io.

let a = [4, 6, 8, 1, 0, 58, 1, 34, 7, 4, 2].
let f = (lambda with (x,y) do return x+(y if y > 10 else 0)).
let s = a @reduce(f,0).
io @println s.

assert (s == 92).


Output:

92


### Challenge: Average of an array¶

> Find the average value of the given array of numbers.

load system io.

let a = [7, 11, 34, 50, 200].
let avg = a @reduce(lambda with (x,y) do return x+y)/a @length().
io @println avg.

assert (avg == 60).


Output:

60


### Challenge: Is an element in a list?¶

> Tell if the given value is in the list.

load system io.

let array = [10, 14, 0, 15, 17, 20, 30, 35].
let x = 17.
io @println ((x+" is in the list") if array @member(x) else (x+" is not in the list")).


Output:

17 is in the list


We can also use a reduction function to solve this,

load system io.

let array = [10, 14, 0, 15, 17, 20, 30, 35].
let x = 17.

if array @reduce(lambda with (acc,i) do return true if i==x else acc,false) do
io @println (x+" is in the list").
else
io @println (x+" is not in the list").
end


Output:

17 is in the list


### Challenge: First odd number¶

> Find the first odd number in a list of integers.

The easiest way to do this is with a reduction,

load system io.

let mod = math @mod.

let array = [2, 4, 18, 9, 16, 7, 10].
let odd = array @reduce(lambda with (acc,i) do return i if type @isnone(acc) and mod(i,2) else acc,none).
io @println odd.


Output:

9


### Challenge: Take every second element¶

> Form a new array by picking every second element from the original array.

load system io.

let array = [20 to 30] @filter(lambda with x do return math @mod(x,2)).
io @println array.

assert (array == [21,23,25,27,29]).


Output:

[21,23,25,27,29]


We can use an index vector to accomplish the same thing,

load system io.

let a = [20 to 30].
let array = a @[1 to a @length()-1 step 2] .
io @println array.

assert (array == [21,23,25,27,29]).


Output:

[21,23,25,27,29]


### Challenge: Number of occurrences in an array¶

> Count how many times a particular element appears in the array.

load system io.

let dt = ["apple",
"pear",
"grape",
"lemon",
"peach",
"apple",
"banana",
"grape",
"pineapple",

let cnt = dt @count("grape").
io @println cnt.

assert (cnt == 2).


Output:

2


### Challenge: Finding unique elements¶

> Print all unique elements of the given array.

Converting a list to a set will remove all duplicate elements in the list.

load system io.

function unique with lst:%list do
return set @toset lst @sort().
end

let a = unique([2, 3, 7, 4, 5, 5, 6, 2, 10, 7]).

io @println a.

assert (a == [2,3,4,5,6,7,10])


Output:

[2,3,4,5,6,7,10]


### Challenge: Minimum and maximum¶

> Find the minimum and the maximum numbers in the given list of integers.

load system io.

function max with lst:%list do
return lst @sort(true) @0.
end

function min with lst:%list do
return lst @sort() @0.
end

let v = [7, 6, 12, 3, 4, 10, 2, 5, 15, 6, 7, 8, 9, 3].

let a = max v.
let b = min v.

io @println a.
io @println b.

assert (a == 15 and b == 2).


Output:

15
2


### Challenge: Increasing sequences¶

> Check if the given array contains increasing (or decreasing) numbers.

load system io.

let a = [3, 7, 19, 20, 34].
let b = type @toboolean(a @reduce(lambda with (x,y) do return y if x<y else false)).

io @println b.

assert (b).


Output:

true


## Section: Multi-Dimensional Data¶

### Challenge: Transpose a matrix¶

> Take a matrix and print its transposed version.

In Asteroid a matrix can be represented by nested lists, like so,

let m = [[1,2],
[3,4]].


The transpose of this matrix is,

let m = [[1,3],
[2,4]].


In a square matrix computing the transpose is just a matter of swapping around the elements. However, here we will solve the more general problem for non-square matrices,

let m = [[1,2],
[3,4],
[5,6]].


with its transpose,

let m = [[1,3,5],
[2,4,6]].


The procedure:

load system io.

function transpose with m do
-- figure out the dimensions
let xdim = m @0 @length().
let ydim = m @length().

-- reserve space for the transpose
-- first we do the ydim of new matrix
let mt = range(xdim).
for y in mt do
let mt @y = range(ydim).
end

-- swap the elements
for x in range(xdim) do
for y in range(ydim) do
let mt @x @y = m @y @x.
end
end

return mt.
end

function print_matrix with m do
io @println "".
for r in m do
for e in r do
io @print (e + " ").
end
io @println ("").
end
io @println "".
end

let m = [[1,2],
[3,4]].

let mt = transpose(m).

io @println ("The transpose of:").
print_matrix m.
io @println ("is:").
print_matrix mt.
io @println ("").

let m = [[1,2],
[3,4],
[5,6]].

let mt = transpose(m).

io @println ("The transpose of:").
print_matrix m.
io @println ("is:").
print_matrix mt.
io @println ("").

assert(mt == [[1,3,5],[2,4,6]]).


Output:

The transpose of:

1 2
3 4

is:

1 3
2 4

The transpose of:

1 2
3 4
5 6

is:

1 3 5
2 4 6


### Challenge: Sort hashes by parameter¶

> Sort a list of hashes using data in their values.

This task is commonly performed to sort items where the sortable parameter is one of the values in the hash. For example, sorting a list of people by age.

load system io.

let randint = random @randint.

random @seed(42).

-- hash of names with ages
let ht = hash @hash().
ht @insert("Billie",randint(20,50)).
ht @insert("Joe",randint(20,50)).
ht @insert("Pete",randint(20,50)).
ht @insert("Brandi",randint(20,50)).

-- export the hash as a list of pairs
let lst = ht @aslist().

-- define our order predicate on a
-- list of pairs where the second
-- component holds the order info
function pairs with ((_,x),(_,y)) do
return true if x < y else false.
end

-- print out the sorted list
io @println (sort @sort(pairs,lst)).

assert (sort @sort(pairs,lst) == [("Pete",20),("Joe",23),("Billie",40),("Brandi",43)])


Output:

[(Pete,20),(Joe,23),(Billie,40),(Brandi,43)]


### Challenge: Count hash values¶

> For a given hash, count the number of occurrences of each of its values.

For example, a hash is a collection mapping a car’s license plate to the colour of the car or a passport number to the name of the street where the person lives. In the first example, the task is to count how many cars of each colour there are. In the second example, we have to say how many people live on each street. But let’s simply count the colours of fruit.

load system io.

let fruit_hash = hash @hash().
fruit_hash @insert("apple","red").
fruit_hash @insert("banana","yellow").
fruit_hash @insert("grapefruit","orange").
fruit_hash @insert("grapes","green").
fruit_hash @insert("kiwi","green").
fruit_hash @insert("lemon","yellow").
fruit_hash @insert("orange","orange").
fruit_hash @insert("pear","green").
fruit_hash @insert("plum","purple").

let fruit_lst = fruit_hash @aslist().

let color_hash = hash @hash().
for (_,color) in fruit_lst do
if not color_hash @get(color) do
color_hash @insert(color,1).
else
color_hash @insert(color, color_hash @get(color) +1).
end
end
let color_lst = color_hash @aslist().

function pairs with ((_,x),(_,y)) do
return true if x < y else false.
end

io @println (sort @sort(pairs,color_lst)).


Output:

[(red,1),(purple,1),(yellow,2),(orange,2),(green,4)]


### Challenge: Product table¶

> Generate and print the product table for the values from 1 to 10.

We will do this with an outer loop and a map function.

load system io.

function format with v do
let maxlen = 3.
let vstr = type @tostring v.
return [1 to maxlen-len(vstr)] @map(lambda with _ do return " ") @join("") + vstr.
end

for i in 1 to 10 do
io @println ([1 to 10] @map(lambda with x do return format(i*x)) @join(" ")).
end


Output:

 1   2   3   4   5   6   7   8   9  10
2   4   6   8  10  12  14  16  18  20
3   6   9  12  15  18  21  24  27  30
4   8  12  16  20  24  28  32  36  40
5  10  15  20  25  30  35  40  45  50
6  12  18  24  30  36  42  48  54  60
7  14  21  28  35  42  49  56  63  70
8  16  24  32  40  48  56  64  72  80
9  18  27  36  45  54  63  72  81  90
10  20  30  40  50  60  70  80  90 100


### Challenge: Pascal triangle¶

> Generate the numbers of the Pascal triangle and print them.

The Pascal triangle is a sequence of rows of integers. It starts with a single 1 on the top row, and each following row has one number more, starting and ending with 1, while all of the other items are the sums of the two elements above it in the previous row. It is quite obvious from the illustration:

       1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1


To calculate the values of the next row, you may want to iterate over the values of the current row and make the sums with the numbers next to it. Let us use the functional style that the language offers. Consider the fourth row, for example: 1 3 3 1. To make the fifth row, you can shift all the values by one position to the right and add them up to the current row:

  13310
+ 01331
-------
14641


We can easily accomplish this with our vector module. Given the vector of the fourth row,

[1,3,3,1]


we create two new vectors,

[1,3,3,1,0]


and

[0,1,3,3,1]


vector @add([1,3,3,1,0],[0,1,3,3,1]) = [1,4,6,4,1]


The only thing that is left to do is to iterate appropiately and format the output.

load system io.

let triangle = [[1]].
let ix = 0.

for i in 1 to 6 do
let v = triangle @ix.
let v1 = [0] + v.
let v2 = v + [0].
let triangle = triangle + [new_v].
let ix = ix + 1.
end

for r in triangle do
io @println (r @map(lambda with v do return type @tostring v) @join(" ")).
end


Output:

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1


The program prints the first seven rows of the Pascal triangle. The rows are not centred, and are aligned to the left side. As an extra exercise, modify the program so that it prints the triangle as it is shown at the beginning of this task. For example, you can first generate rows and keep them in a separate array and then, knowing the length of the longest string, add some spaces in front of the rows before printing them.