Challenge 1 - Triangle Font

Hi all ! I want to introduce my first challenge – Triangle Font. 

I initially solved it in python as I am a bit more confident in that language then I attempted to solve it in q.  

Problem 1 – Triangle Font 

You are given a positive integer?X. Print a numerical triangle of height?X - 1?like the one below: 

22 

333 

4444 

55555 

….. 

Python Solution:

for i in range(1,int(input())): print((pow(10, i)//9)*i)

This is where I’ve got to in q: 

1 "Type a number: "; //Prompt for user input input: read0 0; //Save user input as variable i: 1; //Assign variable i as 1 output:{[input] //Function with input as parameter each[(floor((10 xexp i)%9)*i);input] //floor = to round down, 10 xexp i = 10 to the power of i, divide by 9, multiplied by i i: i+1; //Increment i by 1 }

I am trying to imitate the iterating of the for loop by using “each” while also incrementing i.  

This is how I visualised my approach, but it is throwing a type error. 

Hoping I can get some suggestions on how else I could approach this problem, and any advice would be greatly appreciated. 

 

Thanks in advance,  

Megan 

Hello!
I saw your python solution and came up with the following.

input: “I”$read0 0 output:{((10 xexp x) div 9) * x} each 1 + til input show each “j”$output

 

 

q){x#'x}1+til 4 ,1 2 2 3 3 3 4 4 4 4 q)tf:{x#'x}1+til ::

 

Breaking it down…

  1. 1+til gives us the natural numbers to whatever
  2. {x#'x} gives us N of each N
  3. Suffixing the three unaries with :: gives us the composition to be known as tf

Nice challenge! Who would like to come to another Vector Dojo?

 Thank you for your reply! I forgot about the casting.

 

 

Or to lose the spaces…

q)-1 “0123456789”{x#'x}1+til 5; 1 22 333 4444 55555

 Perfect! What’s more effective than one line of code?

Can you explain the start  of the code? : 

q)-1 "0123456789"

 

Sure! 

The rest of the expression returns a nested list of integers…

 

q){x#'x}1+til 4 ,1 2 2 3 3 3 4 4 4 4

 

Remove those spaces? If your brain has been trained by the one potato, two potato* approach of C-like languages, it immediately sees a double loop to work through the result. The vector programmer sees an opportunity to index. Indexing has atomic iteration; it does all the looping we need. 

* This delightful expression was used by Joel Kaplan on the Array Cast podcast.

 

q)“0123456789”[{x#'x}1+til 4] ,“1” “22” “333” “4444”

 

Q syntax encourages us to replace nested argument brackets with Index (or Index At for unaries) so we’re not scanning code back and forth. And prefix notation is syntactic sugar for Index At, so the @s can disappear. 

 

q)count[distinct[[.Q.a[3 17 14 15 26 3 4 0 3]]]] 7 q)count@distinct@.Q.a@3 17 14 15 26 3 4 0 3 7 q)count distinct .Q.a 3 17 14 15 26 3 4 0 3 7 q) q)“0123456789”{x#'x}1+til 4 ,“1” “22” “333” “4444”

 

Shifting to vector thinking from one potato, two potato takes practice, which is why the Vector Dojo exists. (It’s also a lot of fun.)

The last step is to remove the double quotes q uses to display strings. (And the comma it uses to mark a 1-item vector.)

Q inherits much of k’s design principle: no unnecessary typing. The console, stdout and stderr are simply the longs 0, 1 and 2, which (along with communication handles) have unary function syntax. Plainly put, a string argument to 1 is written to stdout as a side effect. 

 

q)1 “til 6” til 61

 

But the result of 1 is still 1, and that got written to the console too and followed as usual by a newline. Terminating an expression with a semicolon stops its result being displayed at the console, and is the only good use for a terminating semicolon. (Multiline lambdas use them as separators, which is similar. An expression not followed by a semicolon separator becomes the lambda’s result.) 

 

q)1 “til 6”; til 6q)

 

OK, we ignored the expression result, but we lost the newline too. No unnecessary typing! Instead of joining a newline to the end of each output string, we use the negative of the system handle, which does it for us.

 

q)1 “til 6\n”; til 6 q)-1 “til 6”; til 6 q)-1 “0123456789”{x#'x}1+til 4; 1 22 333 4444

 

Of course stdout and stderr both default to the console, so where is 0 in all this? Well 0 does what the console does: give it a string and it returns the result of evaluating it.

 

q)1+0 “til 6” 1 2 3 4 5 6

 

 

q){b:first x+1;b#x+1}[8;1] 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8

BaiChen’s solution uses the Scan form of the Do iterator \  rather than the Each iterator ' I used. 

Terse q helps us focus on the core of the algorithm. Let‘s use BaiChen’s solution as a practice example.

We can rewrite to eliminate the local variable. And, all other things being equal, good q style prefers infix syntax to bracket notation. 

 

q)8{first[x+1]#x+1}\1 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9

 

The addition surely only needs doing once?

 

q)4{first[b]#b:x+1}\1 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5

 

 On each iteration the vector argument lengthens. But we need increment only its first item.

 

q)4{b#b:1+first x}\1 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5

 

The pattern N f\1 is easily recognisable as: Do f N times, starting with 1. 

Tightening up the algorithm saves CPU, but the Each is faster still.

 

q)\ts:10000 {b:first x+1;b#x+1}[100;1] 438 64560 q)\ts:10000 100{b#b:1+first x}\1 301 63536 q)\ts:10000 {x#'x} 1+til 100 174 63616

 

On the next episode of the Array Cast (just recorded on Tuesday), Michael Higginson talks about how the terse and interactive APL and q REPLs encourage him to compare multiple solutions in a way he would rarely have time to do when writing Java. 

Another alternative

q)\ts:10000 {x#'x} 1+til 100 207 63616 q)\ts:10000 {sums[-1_t]_where t:til 1+x}100 106 130112 q){x#'x}[1+til 100]~{sums[-1_t]_where t:til 1+x}100 1b

However, when applied to a large vector, performance drops off as cut is expensive

q)\ts {x#'x} 1+til 50000 4099 14763251840 q)\ts {sums[-1_t]_where t:til 1+x}50000 6454 31943645248

 

This is my favourite, besides being faster!  All the iteration is implicit. Lovely use of where on an int vector to generate all the items in one hit. 

Implicit iterations tend to be faster than iterators.