APL Quest: 8th Quest Overview#
Welcome to the APL Quest. Today, we’re diving into the details of the 8th Quest for the 2016 round of the APL Problem Solving Competition.
Quest Description#
In this Quest, we are provided with a list of numbers, and our goal is to return a two-element list. The first element must contain all the negative values, while the second element must contain all the non-negative values.
Approach#
We can achieve this by filtering the values. Let’s get started by creating some values.
Implicit Mapping in APL#
APL has implicit mapping over arrays, meaning that any comparison, such as checking if a number is less than or equal to zero, automatically applies to all the values in the array.
In APL, 1
and 0
represent true
and false
, respectively. We can use this property to filter our array V
with the following expressions:
positiveValues ← V / 0 ≤ V
negativeValues ← V / V < 0
This can also be expressed using a single function that returns both negative and non-negative values:
filterValues ← {((0 > ⍵)/⍵)((0 ≤ ⍵)/⍵)}
Now, we’re equipped with two formulas that we can place next to each other.
Creating a Function#
To turn this into a function, we encapsulate it in braces, forming an anonymous lambda function. We designate the argument as Ω
, the rightmost letter of the Greek alphabet, since our argument will be placed on the right:
filterValues ← { (V / 0 ≤ V), (V / V < 0) }
This expression forms one solution, but it has a lot of parentheses, which can reduce readability. Alternatively, we can define the function using the explicit filtering approach with a mask:
filterValues ← { (⍵/⍨0>⍵)(⍵/⍨0≤⍵) }
Simplifying the Expression#
To simplify, we can create a function that takes the mask on the right and the data on the left. Although APL does not have a built-in function for this, we can utilize a modifier called commute (or swap), which rearranges the function arguments. We redefine our expression as follows:
filterValues ← { (M ← ⍵/0≤⍵), (⍵/M) }
However, since we want to filter values and keep track of them efficiently, we can store the mask as M
directly. Notably, the result of the assignment in APL is always the value being assigned.
Avoiding Redundant Calculations#
To optimize further, we can use a positive mask instead and negate it when needed. Our expression reads:
positiveMask ← ⍵ / 0 ≤ ⍵
negativeValues ← ⍵ / ~positiveMask
Alternatively, we could declare masks separately, enhancing code clarity:
negativeMask ← ⍵ / ⍵ < 0
positiveMask ← ⍵ / 0 ≤ ⍵
In this way, we have neatly organized our logic.
Set Computations Variation#
For a slightly different approach, we can switch our focus to directly computing the positive values first:
positiveValues ← ⍵ / 0 ≤ ⍵
negativeValues ← ⍵ \ positiveValues
In this case, we employ the tilde (~) operator to denote “without,” distinguishing between not in logical contexts and except in set difference contexts.
Conclusion#
This strategy leads us to a final solution that efficiently separates our values into negative and non-negative categories. We thank you for watching, and we hope this guide has clarified the process of tackling this Quest in APL!