Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Ali Faizan Kazmi
Ali Faizan Kazmi

Posted on

     

Koping with KDB 002: More List Reversal

Estimated reading time: 10 minutes

In the previouspost we stopped short of applying recursion to reverse a list since we found a quirkier way to do it. Let's see if the use of recursion results in simpler code.

Give me yourifs, yourelses...

Yes, it is possible to control a Q script's execution using conditional evaluation, although it is discouraged: restructuring your code to avoid conditional evaluation is preferred whenever possible.

Available to us is theif statement:

n: 1if[n=1;message: "I don't think this is useful to us"]message-> "I don't think this is useful to us"

The if statement evaluates a given condition (n=1) and if the condition evaluates to a value greater than 0 (which represents the booleanfalse) then any statements following the condition are executed in order from left to right. In our case, we are assiging the string"I don't think this is useful to us" to the variablemessage.


As an aside, notice that the concept of scope doesn't apply here: the variablemessage is defined inside theif statement but continues to be accessible outside of it!


Yes, this doesn't fit our use case: if we are to use recursion, we also need anelse clause - something like the following:

if base case reached    returnelse    continue list reversal

...or just give me your ternaries

Q supports an equivalent of the ternary operator? available in languages such as Java, JavaScript and C#:

$[1b;"Execute if true";"Execute if false"]-> "Execute if true"$[0b;"Execute if true";"Execute if false"]-> "Execute if false"

The first expression given to the$ operator (1b or0b) is the condition to evaluate. If the condition evaluates to true then the second expression is executed, otherwise the third one is. If you want to execute multiple expressions for one condition then you can encapsulate your expressions in square brackets:

$[1b;[a:2;b:3];c:4]a-> 2b-> 3

I think we know enough to proceed with reversing our list. Onward to recursion!

A simple(r) example

Before we apply recursion to our problem, we can try testing it on a simpler problem to make sure it works as expected. We can define a recursive function which keeps adding 1 to a given number until the number is equal to 6:

recur: {[number] $[number=6;"Done";recur[1 + number]]}

Looks like it works!

recur 1-> "Done"

Can we be sure that recursion is taking place, though? Since I'm not aware of the equivalent ofprintln in Q, one way to make sure that recursion is in fact happening is to pass a number greater than 6 to the function. In languages like Java this should generate a stack overflow since the recursive function would keep on calling itself until there was no more space on the stack. That's exactly what happens here, too:

recur 7-> `stack-> @-> {[number] $[number=6;"Done";recur[1 + number]]}-> 2008

No idea what the@ and2008 mean. I'll probably come back to how Q displays exceptions later (Q exceptions prefer to be addressed as "signals").

Finally, recursion

No, wait. There's one more operation we need before we can construct our function: thejoin operator, used to join two lists. The operator is simply a comma:

1 2 3,4 5 6-> 1 2 3 4 5 61,2 3 4-> 1 2 3 4

Sorry, one more thing: we also need a way to reduce the size of the list with each recursive call until we're left with a list of one element. Enter thecut (_) operator:

list: 1 2 31 _ list-> 2 3-1 _ list-> 1 2

At long last, here's our function:

reverseList: {[list]     $[1 = count list;        list;        reverseList[1 _ list],-1 _ list    ]}reverseList 1-> 1reverseList 1 2-> 2 1reverseList 1 2 3-> 3 2 1 2

Uh-oh, the last result is clearly wrong. Why? Let's do a dry run:

reverseList 1 2 3 -> call reverseList[2 3]reverseList 2 3 -> call reverseList 3reverseList 3 -> return 3reverseList 2 3 -> return 3 joined with 2reverseList 1 2 3 -> return 3 2 joined with 1 2

Therein lies the fault: it would've been more obvious if I had formulated a proper algorithm:

Given a list LIts reverse is a reverse of the list t(L)     (where t denotes the tail - e.g., t(1 2 3) is 2 3) Joined with h(L)     (where h denotes the head - e.g., h(1 2 3) is 1)The reverse of a list with one element is the list itself

In short, the last expression we passed to the$ operator is wrong. We should've used thetake (#) operator:

list: 1 2 31#list-> ,1

The comma before1 is just a way of indicating that1 is not a number: it is a single-item list.

Here goes nothing:

reverseList: {[list]     $[1 = count list;        list;        reverseList[1 _ list],1#list    ]}reverseList 1-> 1reverseList 1 2-> 2 1reverseList 1 2 3-> 3 2 1reverseList "A string is a list of characters"-> "sretcarahc fo tsil a si gnirts A"reverseList "Sigh of relief"-> "feiler fo hgiS"

Detour: Q'sreverse

I mentioned earlier that Q already has areverse function. What does it look like? We can type the function name in the Q prompt to find out:

reverse-> |:

Is|: considered one operator? Is it two? Why isn't there an argument in the function body? Is|: meant to precede a list or succeed it? So many questions. We could answer the last one:

|: 1 2 3-> `1 2 3 |:-> 3 2 1

Since|: 1 2 3 returns a signal, looks like the correct way to use|: is definitely the second one. A cursory look at the various Q operators doesn't yield any answers as to how|: is interpreted. Maybe it's an operator (or two operators) coming fromk, a language upon which Q is based. I have no difficulty putting this off for later (apologies if you do!).

Tail Recursion

Our recursive function isn'ttail-recursive. Let's try to change it into one. We need some sort of an accumulator which will keep a running state of the reversed sublist. Once we get to a list of one element, we will simply join that element with the reversed sublist and make it the new head of the list. The following is a dry-run to demonstrate what I mean:

reverseList 1 2 3 -> call reverseList[2 3] with accumulator set to 1reverseList 2 3 -> call reverseList 3 with accumulator set to 2 1reverseList 3 -> end of list, return 3 joined with accumulator

Here's our tail-recursive function:

reverseList: {[list;acc]     $[1 = count list;        list,acc;        reverseList[1 _ list;(1#list),acc]    ]}

Tail recursion makes the function slightly more difficult to follow, but it does go easy on the stack.

reverseList[1 2 3;()]-> 3 2 1reverseList[`a`b`c`d;()]-> `d`c`b`a

Notice the ugly() that we need to pass as the initial value of the accumulator -() denotes an empty list. We can clean up the function by hiding the() in an inner function:

reverseList: {[list] reverseListInner[list;()]}reverseListInner: {[list;acc]     $[1 = count list;        list,acc;        reverseListInner[1 _ list;(1#list),acc]    ]}

Our solution obviously isn't as succinct as Q's, but we did learn the$,_, and# operators along the way.

reverseList 1 2 3-> 3 2 1reverseList 1-> ,1

Ugh, looks like our function returns a list of one item when it's passed that item as an argument. Not really what we want. We could handle that case in our outer function:

reverseList: {[list]     $[1 = count list;        list;        reverseListInner[list;()]    ]}reverseList 1-> 1reverseList 1 2 3-> 3 2 1

To sum up, here's our solution:

reverseList: {[list]     $[1 = count list;        list;        reverseListInner[list;()]    ]}reverseListInner: {[list;acc]     $[1 = count list;        list,acc;        reverseListInner[1 _ list;(1#list),acc]    ]}

It's ugly to make the same check (1 = count list) in two places for different purposes, but I'll call it a day at this point.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Glasgow
  • Education
    MSc. Advanced Computing Science
  • Work
    Technology Associate at Morgan Stanley
  • Joined

More fromAli Faizan Kazmi

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp