Sunteți pe pagina 1din 69

101 LINQ to Objects Samples: From C# to APL+Win

Ajay Askoolum

Language Integrated Query (LINQ) is an extension to C#, available from version 3.01, that is, with Visual
Studio 2008; LINQ is a standard for querying and updating data. The syntax of LINQ integrates with that of the
host language, C# or Visual Basic.Net. Here, I am interested in LINQ to Objects—the other implementations
are LINQ to SQL, LINQ to Entities, LINQ to DataSet, and LINQ to XML—that deals with in-memory data or,
more generally, collections.

APL+Win, A Programming Language (APL) has been about language integrated data manipulation—
arrays, vectors, and nested or jagged arrays—throughout its history2. Here I am interested in LINQ to Objects
as a formal deliverable from APL+Win. With its strong/unique integration of Component Object Model (COM)
technology into its language features, APL+Win delivers LINQ functionality either in conjunction with C# as a
COM Server or on a standalone basis.

In this article, I present LINQ to Objects implemented in C# and APL+Win side by side. Above all, LINQ
provides a platform for extolling the capabilities of APL+Win using the same vocabulary as a C# developer!
The small price of this opportunity is the acquisition of a grasp of the LINQ to Objects concepts.

I urge you to read the ‘Partitioning Operators’ section even if LINQ to Objects is not within your sphere of
interest right now. For APL+Win users, this group of operators will be very familiar. That C# is now
incorporating these features into the language does indeed suggest that APL+Win is a language that is ahead
of time.

Table of Contents

Table of Contents ........................................................................................................................ 1


1.1 The Agenda ...................................................................................................................... 5
1.1.1 The terms of reference...............................................................................................................6
1.1.2 The payoff for general APL+Win programming .........................................................................6
1.1.2.1 Replicating the experience...................................................................................................................7
1.1.3 APL+Win and Dot Net Accessibility ...........................................................................................8
1.1.4 Why LINQ to Objects? ...............................................................................................................9
1.2 Getting Started.................................................................................................................. 9
1.2.1 Understanding the code.............................................................................................................9
1.2.1.1 Language symbols .............................................................................................................................10
1.2.1.2 What is the question?.........................................................................................................................11
1.3 Restriction Operators...................................................................................................... 11
1.3.1 Where - Simple 1 .....................................................................................................................11
1.3.1.1 C# code ..............................................................................................................................................11
1.3.1.2 APL+Win code ..................................................................................................................................12
1.3.1.3 Understanding the standard result .....................................................................................................12
1.3.1.4 Method Signature ..............................................................................................................................13
1.3.1.5 Further tests .......................................................................................................................................14
1.3.2 Where - Simple 2 .....................................................................................................................14
1.3.3 Where - Simple 3 .....................................................................................................................16

1
Dot Net Framework 3.5.
2
Unfortunately, LINQ and APL+Win use different jargon to describe identical concepts; I expect that the LINQ vocabulary will prevail.
© Ajay Askoolum
1.3.4 Where - Drilldown ....................................................................................................................17
1.3.5 Where - Indexed ......................................................................................................................18
1.4 Projection Operators....................................................................................................... 19
1.4.1 Select - Simple 1......................................................................................................................19
1.4.1.1 Linq6 in focus....................................................................................................................................19
1.4.2 Select - Simple 2......................................................................................................................20
1.4.3 Select - Transformation............................................................................................................20
1.4.4 Select - Anonymous Types 1 ...................................................................................................21
1.4.5 Select - Anonymous Types 2 ...................................................................................................21
1.4.6 Select - Anonymous Types 3 ...................................................................................................22
1.4.7 Select - Indexed .......................................................................................................................22
1.4.8 Select - Filtered........................................................................................................................23
1.4.9 SelectMany - Compound from 1 ..............................................................................................23
1.4.10 SelectMany - Compound from 2 ............................................................................................24
1.4.11 SelectMany - Compound from 3 ............................................................................................24
1.4.12 SelectMany - from Assignment ..............................................................................................25
1.4.13 SelectMany - Multiple from ....................................................................................................26
1.4.14 SelectMany - Indexed ............................................................................................................27
1.5 Partitioning Operators..................................................................................................... 28
1.5.1 Take - Simple...........................................................................................................................28
1.5.2 Take - Nested ..........................................................................................................................28
1.5.2.1 First two customers in Washington and all their orders ....................................................................29
1.5.2.2 First 3 customers in Washington and their first 5 orders...................................................................29
1.5.3 Skip - Simple............................................................................................................................29
1.5.4 Skip - Nested ...........................................................................................................................29
1.5.5 TakeWhile - Simple..................................................................................................................30
1.5.6 TakeWhile - Indexed ................................................................................................................30
1.5.7 SkipWhile - Simple...................................................................................................................31
1.5.8 SkipWhile - Indexed .................................................................................................................31
1.6 Ordering Operators......................................................................................................... 31
1.6.1 OrderBy - Simple 1 ..................................................................................................................31
1.6.2 OrderBy - Simple 2 ..................................................................................................................32
1.6.3 OrderBy - Simple 3 ..................................................................................................................32
1.6.3.1 Where is the difference? ....................................................................................................................32
1.6.4 OrderBy - Comparer ................................................................................................................33
1.6.5 OrderByDescending - Simple 1 ...............................................................................................33
1.6.6 OrderByDescending - Simple 2 ...............................................................................................33
1.6.7 OrderByDescending - Comparer .............................................................................................34
1.6.8 ThenBy - Simple ......................................................................................................................34
1.6.9 ThenBy - Comparer .................................................................................................................35
1.6.10 ThenByDescending - Simple .................................................................................................35
1.6.11 ThenByDescending - Comparer ............................................................................................35
1.6.12 Reverse..................................................................................................................................36
1.7 Grouping Operators ........................................................................................................ 36
1.7.1 GroupBy - Simple 1..................................................................................................................36
1.7.2 GroupBy - Simple 2..................................................................................................................37
1.7.3 GroupBy - Simple 3..................................................................................................................37
1.7.4 GroupBy - Nested ....................................................................................................................38
1.7.5 GroupBy – Comparer...............................................................................................................38
1.7.6 GroupBy – Comparer, Mapped................................................................................................38
1.8 Set Operators ................................................................................................................. 39
1.8.1 Distinct - 1 ................................................................................................................................39
1.8.2 Distinct - 2 ................................................................................................................................39
1.8.3 Union - 1 ..................................................................................................................................40
1.8.4 Union - 2 ..................................................................................................................................40
1.8.5 Intersect - 1 ..............................................................................................................................41
1.8.6 Intersect - 2 ..............................................................................................................................41
1.8.7 Except - 1.................................................................................................................................42
1.8.8 Except - 2.................................................................................................................................42
Page 2 of 69
1.9 Conversion Operators..................................................................................................... 43
1.9.1 To Array ...................................................................................................................................43
1.9.2 To List ......................................................................................................................................43
1.9.3 To Dictionary............................................................................................................................43
1.9.4 OfType .....................................................................................................................................44
1.10 Element Operators........................................................................................................ 44
1.10.1 First - Simple..........................................................................................................................44
1.10.2 First – Condition (missing at URL) .........................................................................................45
1.10.3 First - Indexed ........................................................................................................................45
1.10.4 FirstOrDefault - Simple ..........................................................................................................45
1.10.5 FirstOrDefault - Condition ......................................................................................................46
1.10.6 FirstOrDefault - Indexed.........................................................................................................46
1.10.7 ElementAt ..............................................................................................................................47
1.11 Generation Operators ................................................................................................... 48
1.11.1 Range ....................................................................................................................................48
1.11.2 Repeat ...................................................................................................................................48
1.12 Quantifiers .................................................................................................................... 49
1.12.1 Any - Simple...........................................................................................................................49
1.12.2 Any - Indexed.........................................................................................................................49
1.12.3 Any - Grouped........................................................................................................................49
1.12.4 All - Simple.............................................................................................................................49
1.12.5 All - Indexed ...........................................................................................................................50
1.12.6 All - Grouped..........................................................................................................................50
1.13 Aggregate Operators .................................................................................................... 50
1.13.1 Count - Simple .......................................................................................................................50
1.13.2 Count - Conditional ................................................................................................................51
1.13.3 Count - Indexed .....................................................................................................................51
1.13.4 Count - Nested.......................................................................................................................52
1.13.5 Count - Grouped ....................................................................................................................52
1.13.6 Sum - Simple .........................................................................................................................52
1.13.7 Sum - Projection ....................................................................................................................53
1.13.8 Sum - Grouped ......................................................................................................................53
1.13.9 Min - Simple ...........................................................................................................................53
1.13.10 Min - Projection ....................................................................................................................54
1.13.11 Min - Grouped ......................................................................................................................54
1.13.12 Min - Elements .....................................................................................................................54
1.13.13 Max - Simple ........................................................................................................................55
1.13.14 Max - Projection ...................................................................................................................55
1.13.15 Max - Grouped .....................................................................................................................55
1.13.16 Max - Elements ....................................................................................................................56
1.13.17 Average - Simple .................................................................................................................56
1.13.18 Average - Projection ............................................................................................................56
1.13.19 Average - Grouped ..............................................................................................................57
1.13.20 Fold - Simple........................................................................................................................57
1.13.21 Fold - Seed ..........................................................................................................................57
1.14 Miscellaneous Operators .............................................................................................. 58
1.14.1 Concat - 1 ..............................................................................................................................58
1.14.2 Concat - 2 ..............................................................................................................................58
1.14.3 EqualAll - 1.............................................................................................................................58
1.14.4 EqualAll - 2.............................................................................................................................59
1.14.4.1 EqualAll - 2 : Extension ..................................................................................................................59
1.15 Custom Sequence Operators ....................................................................................... 59
1.15.1 Combine.................................................................................................................................60
1.15.1.1 C#:APL+Win comparison ...............................................................................................................60
1.16 Query Execution ........................................................................................................... 61
1.16.1 Deferred .................................................................................................................................61
1.16.2 Immediate ..............................................................................................................................61
1.16.3 Query Reuse..........................................................................................................................62

Page 3 of 69
1.17 Time for 101 APL+Win samples? ................................................................................. 62
1.17.1 Some eligible topics ...............................................................................................................62
1.17.1.1 Example 1 - Return the sign of all elements of a numeric vector/array ..........................................62
1.17.1.2 Example 2 - Return the magnitude indicator of all elements of a numeric vector/array.................62
1.17.1.3 Example 3 – Apply the percentage of increase ...............................................................................62
1.17.1.4 Example 4 – Re-calculate the diagonal elements of a numeric array..............................................63
1.17.1.5 Example 5 – Return the powers of 2 of each element of a numeric vector/array............................63
References ................................................................................................................................. 63
Appendix A – Product & Customer Lists ................................................................................ 65
Index ........................................................................................................................................... 67
Code Listings............................................................................................................................. 69

Page 4 of 69
101 LINQ to Objects Samples: From C# to APL+Win
Ajay Askoolum
1.1 The Agenda
The agenda is set at the following Universal Resource Locator (URL):

http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx

• Alas, the material at his URL does not refer to APL+Win at all, as to be expected!

© Ajay Askoolum
• A much more serious criticism is that the URL does not provide complete code samples. A number of
the code snippets do not lend themselves to simple copy & paste into another project; some of the
sample codes are also ‘incorrect’3. Although the basic code listings are useful, the supporting code
4
dependencies are missing .

• The code samples simply demonstrate concepts5: the results of sample queries (written to the
console) shown at the URL provide valuable insight into the functionality, especially from the point of
view of replicating them in APL+Win.

• If you count the number of samples, there are fewer than the 101 suggested by the title; and a
number of them—such as Linq60—do not work. This is not only misleading but also highly
unsatisfactory, not least because of the pedigree of its origin—Microsoft—and the fact that this passes
for reference material.

In spite of all the shortcomings, this provides an excellent opportunity for exploring a brand new feature of
the contemporary flagship language; ore to the point, this provides the terms of reference for illustrating
APL+Win techniques that have been part of the language from the outset. I have included the missing
samples in this article.
1.1.1 The terms of reference
The code samples at the URL have the data that they manipulate either hard-coded or drawn from supporting
functions. The functions are not parameterised.

My objective is to write a COM server in C# and use it with APL+Win, and to compare the results from C#
with those from APL+Win, in the APL+Win immediate mode. In order to extend the scope of this exercise,
some of the methods in C# and APL also accept arguments in client/server mode.

This serves several purposes.

• C# uses complex data types such as the product and customer lists; although these are akin to
APL+Win’s nested arrays, there is an important difference. C# refers to the data items by name,
something that is not possible in APL+Win. In the APL+Win environment, I refer to the data items by
their position or cardinal order in the nested array. Therefore, I also need to coax C# to supply the
data in a simpler nested/jagged array format for use by APL+Win.

• A number of refinements in the C# examples are not obvious from the examples given. For example,
UNION disregards duplicated elements in its result; appropriate arguments to such functions can
highlight such features.

• Overall, this demonstrates C# and APL+Win in a fully functional client/server configuration—an option
for any application.
1.1.2 The payoff for general APL+Win programming
LINQ techniques are interesting from point of view of APL+Win for several reasons.

• All data in APL+Win are objects or, for a simplistic view, arrays; LINQ deals with such objects.

3
For example, the code for the class CustomSequenceOperators is incorrect; Linq96 shows wordsA.EqualAll(wordsB) which should be
wordsA.SequenceEqual(wordsB).
4
For example, functions such as GetProductList and GetCustomerList, are missing.
5
The code outputs results to the console: as such, modifications are required to incorporate them in class libraries so that they can take arguments and
return results.

Page 6 of 69
• Although C# and APL+Win operate in disparate environments, this paper demonstrates how well the
two can work together (the prospects for Visual APL which works from the same environment as C#
are even better!).

• I have not developed optimised code for my purpose or designed code for re-use. However, there are
subtle opportunities to harvest APL+Win idioms. The APL+Win code samples generally use one-
dimensional arrays (vectors or nested vectors): they can be generalised for multi-dimensional arrays.

• There is an opportunity to familiarise yourself with another jargon, that of LINQ (and C#): this will be
useful for the future in many ways, even if this is not apparent right now.
1.1.2.1 Replicating the experience
I am using APL+Win version 9, with Visual Studio 2008 with Service Pack 1, under Windows XP Professional
with Service Pack3.

The APL+Win workspace, LINQ to OBJECTS.W3, is accessible with version 9 of the interpreter.

If you have Visual Studio 2008 (any hybrid), one of your options is to attempt to load the project and
recompile it: I have not tested this project with every version of Visual Studio 2008. This will register the COM
Dynamic Library (DLL) as required, without further intervention from you.
6
If you do not have Visual Studio 2008 , you will need to do the following:

• Download and install Dot Net Framework 3.5

• Register the COM DLL, LINQ.DLL, as follows:

????

Depending on how you use the supplied code, you may receive the following (or similar) error:

ŒWI ERROR: mscorlib exception 80070003 Could not find a part of the path
'C:\AJAY\C#\VS2008\VS2008\LINQ to Objects\LINQ\customers.xml'.

This arises as a result of a hard-coded path reference is DATA.CS. The resolution is to ensure that the
7
path specified reflects the location of CUSTOMERS.XML in your preferred directory structure .

The freely downloadable XML NOTEPAD 2007, see below, is a useful tool for creating and examining
XML files interactively. You may want to use this tool to examine/audit the results of LINQ queries that use
CUSTOMERS.XML.

6
This presents the same scenario as that when deploying your own Dot Net DLL.
7
The project needs re-compilation after any changes to the code.

Page 7 of 69
1.1.3 APL+Win and Dot Net Accessibility
An often raised complaint, perhaps more often than necessary given the respective infrastructure and lineage
of APL+Win and C#, is that APL+Win does not, but should, implement a system function, ŒNET, to enable
access to the Dot Net Libraries. I do not subscribe to this view for the following reasons:

• Such an accessibility function will not enhance or optimise the existing legacy of APL+Win
applications such that they can benefit from enhancements in the technology that Dot Net heralds. It
might be useful for future development: in this context, Visual APL offers better prospects for return on
investment since it is closely aligned to APL+Win and is a true Dot Language, not just a Dot NET
‘aware’ one.

• Such a function allows access to the Dot NET foundation class libraries only and not to Dot NET
languages such as C#8. Quite often, it is necessary to write APL Code to glue calls to the foundation
class libraries. This implies that a managed and strongly typed environment (Dot Net) is coerced into
an un-managed and loosely typed environment (APL). This has strong implications for application
debugging and maintenance. Besides, in such as scenario APL is always playing catch up to
improvements in the Dot Net framework.

• APL+Win offers a robust COM interface that enables any user-built DLL to be deployed from within
APL+Win. Therefore, the Dot NET environment can be kept separate from the APL+Win environment
with several advantages: first, it is not the APL developer who has to code the Dot Net functionality—a
Dot Net expert can do it independently of APL—and second, each environment can be debugged
separately or together. Play the media file APLWINDEBUGSC#.WMV for an illustration.

8
Remember that LINQ fits within a Dot NET language, such as C# or Visual Basic.NET.

Page 8 of 69
1.1.4 Why LINQ to Objects?
LINQ to Objects is especially interesting from an APL point of view.

• It is a set of standard operators that eliminates the need for the developer to write loops
1.
• It standardises the means of doing routine tasks relating to data in memory and has a syntax that is
very much like that of the host language.

• Is that not what APL+Win is (has always been) about?


1.2 Getting Started
The general format of the illustrations below is to state the purpose of the code sample, list the C# code
sample, list the APL+Win function and then to illustrate and discuss the results.

The 101 code samples are named Linq1 to Linq101, as seen at the URL; I have retained this convention in
coding the APL+Win counterpart functions in order to maintain a 1:1 correspondence.

• For some functions, there is an extension function, named LinqnEx (n designates the numeric
sequence of the code sample): I have used this to elaborate on extensions to the functionality in
question.

• The source data for the code samples, where required, is internally supplied as complex data types to
the C# functions: GetProductList and GetCustomerList are two such examples. I have coded
GetProductListEx and GetCustomerListEx which supply the same data as a simple type to APL+Win.
9
Please refer to the end of this document for a complete listing of the C# code—EXPLORE.CS and
DATA.CS—that underlies the COM DLL.

For the sake of clarity as well as completeness, I recommend that you refer to the code sample at the URL
and this document concurrently and make appropriate allowance for the errors and omission I have
highlighted.

Given that the C# COM DLL, LINQ.DLL, is registered, the following function establishes an instance
thereof; it runs as the latent expression on loading the workspace LINQ to OBJECTS.W3 and should persist
for the duration of the session:

’ LINQ
[1] © Ajay Askoolum
[2] Œwself„'lq' Œwi 'Create' 'LINQ.Explore'

The first example, discussed next, is deliberately verbose: this is to familiarise you with the pattern for all
the other samples. Therefore, you should study this example first and then may navigate to any other sample,
if necessary.
1.2.1 Understanding the code
These pages do not contain a tutorial on either C# or APL+Win: such a task is beyond the scope of this
exercise. However, the VS2008 project and the APL+Win workspace contain all the code used herein.

When working through these pages, it may be useful to bear in mind the following:

• Unlike APL+Win which has index origin default to zero, C# always works with index origin set to
zero—there is no other option. The APL+Win code I have used here is index-origin independent.

9
I printed the code from Visual Studio 2008 and appended to this PDF.

Page 9 of 69
• Both APL+Win and C# are case sensitive.

• Both APL+Win and C# use square brackets for indexing; for indexing multi-dimensional arrays,
APL+Win uses semi-colon and C# uses comma to separate the co-ordinates.

• The syntax for localisation or scope determination is different in the two languages. With APL+Win,
any name included in the header of a function and separated by a semi-colon has the effect of
restricting the scope of that name to that function only10. In C#, any name included within a pair of
opening and closing braces is local.

• In C#, a comma separates elements of an array/vector; the same is true (but optional) for APL+Win
numeric elements (although it is more conventional to use spaces) of an array/vector.

• Unlike APL+Win which uses single and double quotes—in pairs—interchangeably, C# uses double
quotes for literals or strings and single quotes for data type char.

• C# uses \ as an escape character, embedded in literals; for example, “Prime\r\nExample” indicates


that carriage return and line feed separate the two words. C# has verbatim strings; these are prefixed
by @ and simply instruct C# to take a literal at face value. For example, with @“Prime\r\nExample”,
C# will not interpret \r\n as carriage return and line feed. If you specify a string, say a path as c:\ajay,
the options are to write either “c:\\ajay” or @”c:\ajay”. APL+Win does not have embedded escape
sequence characters but can pass literals to C# using the C# conventions.

• How many statement delimiters does APL+Win have? It might surprise you to learn that there are two:
diamond (ª) and linefeed. C# has a single statement delimiter, semi-colon; statements may span
several lines. Unlike APL+Win where these delimiters are optional, the C# delimiter is mandatory.

If you are lured into the belief that C# is difficult, it might be an opportune moment to remind yourself how
difficult APL has been between the time when you started to learn it and the present, when you have
mastered the language.

C# is a pleasant experience. From my point of view, C# is no more difficult than APL+Win and the two
languages share a common attribute, namely, it is possible to write code without understanding all the
subtleties of the language. Unlike APL+Win, C# has a wealth of sources for worked examples and general
reference; therefore, in theory, C# should be easier to learn.
1.2.1.1 Language symbols
This article involves two languages, C# and APL+Win. The context involves the examination of the code side-
by-side. As an aid to understanding the code, the following table is useful.

APL+Win C# Description
„ = Comes from or becomes
= == Equality comparison
¬ != Not equal to
< < Less than
ˆ <= Less than or equal to (not greater than)
> > Greater than
‰ >= Greater than or equal to (not less than)
| % Modulus

10
In order to keep this simple, I will not elaborate on dynamic localisation here; this does not exist in C#.

Page 10 of 69
1.2.1.2 What is the question?
The question mark is part of the C# language; it is like an APL+Win primitive.

double?[] doubles = { 1.7, 2.3, 4.1, 1.9, null, 2.9 };

In the above line, the question mark indicates that any element of the vector can have a null value.

Sign = (MyVal == 0) ? 0 : (MyVal < 0) ? -1 : 1;

In the latter example, the question mark indicates ‘if’: read the line as “the sign of a scalar MyVal is 0 if it is
zero, else if it is less than one, the sign is -1 else it has a default value of 1”

C# has a data type that is also of interest as it occurs in the code samples: null. C# is a strongly typed
language; APL+Win is a type-inferred language. The null data type is a difficult concept; technically, it means
a missing value or that a variable has been declared but not assigned a value.

APL cannot have a variable that is unassigned since it is not a declarative language. However, in some
contexts, 0/0 or 0/’’ are appropriate to emulate a null value in APL+Win—it also has a null value.

1.3 Restriction Operators


This group of operators apply relational criteria to data and return the elements that satisfy the criteria; the key
concept is to elicit results using relational operators on lists, or vectors, or arrays, numeric and character.
Another way to understand this group of operators is to think of them as filtering an existing object, the
argument, and creating a new object, the result.

APL+Win supports the full set of relational operators found in C#; the difference is that LINQ also has
names for some of the operators whereas APL+Win has primitives.
1.3.1 Where - Simple 1
This example shows the selection, from a given vector, of all elements whose value is greater than five.
1.3.1.1 C# code
At the URL, this sample is listed as follows:

public void Linq1()


{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNums = from n in numbers
where n < 5

Page 11 of 69
select n;
Console.WriteLine("Numbers < 5:");
foreach (var x in lowNums)
{
Console.WriteLine(x);
}
}

The following is apparent:

• The data that passes for the argument to the function is hard-coded, making it inconvenient, if not
impossible, to test the function with alternative data.

• The function does not return a result; it writes to the console. Therefore, it would be impossible to
return results to or pass data from APL+Win.

Therefore, in order to overcome these limitations, I have modified the code to read as follows:

public int[] Linq1(int[] numbers, int threshold)


{
var lowNums = from n in numbers
where n < threshold
select n;
return lowNums.ToArray();
}

Now the function will

• Accept two arguments, the data and the threshold that is used to filter the data.

• Return a result instead of printing it to the console.


1.3.1.2 APL+Win code11
All the APL+Win functions assume that the instance of the C# COM DLL exists already and that it is the
default left-hand argument of Œwi the function.

’ Z„Linq1;L;R;Linq;APL
[1] © RESTRICTION OPERATOR: Where - Simple1
[2] L„¹(2?10)?¨15 © With possible replication
[3] R„?10
[4] Linq„Œwi 'Linq1' L R
[5] APL„(L<R)/L
[6] Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)

1.3.1.3 Understanding the standard result
The first objective must be to reproduce the results from C#:

Œwi 'Linq1' (5 4 1 3 9 8 6 7 2 0) (5)


4 1 3 2 0

The APL+Win result, side by side, is:

11
It would be nice if APL+Win retained syntax colouring with copy & paste, as C#!

Page 12 of 69
The sample shows the results as follows:

On the face of it, the sample shows the result as an n by 1 array whereas the APL equivalent shows it as
a vector of n elements. In fact, the sample is appending a carriage return12 after each element: the results
are identical.
1.3.1.4 Method Signature
The previous example illustrates how a method inside the COM is callable from APL+Win with arbitrary
arguments. However, you should verify how the signature of a method—how it is coded—before attempting a
call.

The syntax of a method may be established by a visual examination of the code—refer to Appendix A
either for code listings—or as follows:

Œwi '?Linq1'
XLinq1 method:
Result@Array_Long „ ŒWI 'XLinq1' numbers@Array_Long threshold@Long

12
WriteLine appends \r\n (carriage return and line feed) to its argument.

Page 13 of 69
Note that this query returns both the name and type of each argument, and indicates the type of the result
returned, if any.
1.3.1.5 Further tests
The APL+Win function, Linq1, creates random arguments—see lines [2]-[3]—and permits tests to be run
quickly.

The following illustrates the results of three further tests.

The argument numbers is listed first (as L), followed by the argument threshold (as R). Then the result
returned by the COM DLL is listed (as Linq), followed by the corresponding result from APL+Win (as APL).
The label Matched? shows 1 when the results from C# and APL+Win match else it shows 0.

This concludes the basic guide to reading each of the remaining examples/samples13.
1.3.2 Where - Simple 2
This query lists all the products that are out of stock. The list or product, unlike the list of customers held in an
external XML file, is built using code in DATA.CS. The first two products are built with these lines of code:

C# accesses the individual elements of data by name, shown here in the VS2008 Immediate Window.

13
This is LINQ! an advancement to C#. .APL+Win has done this from inception, has it not?

Page 14 of 69
This is a complex C# data type; it comprises of several simple C# types. The data structure Product is
build as follows:

public class Product


{
public int ProductID;
public string ProductName;
public string Category;
public decimal UnitPrice;
public int UnitsInStock;
}

In APL+Win, the simplest approach is to code the C# COM DLL to supply the data14. Everything is
available except that the names of the individual elements.

14
This avoids transcription errors and facilitates comparisons.

Page 15 of 69
Finally, let us see the results of the LINQ Query in APL+Win:

’ Z„Linq2;L;R;Linq;APL;products
[1] © RESTRICTION OPERATOR: Where - Simple 2
[2] Linq„Œwi 'Linq2'
[3] products„Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4] L„¹¯1†¨products © Last element is UnitsInStock
[5] APL„(Œio+1)œ¨(L=0)/products
[6] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The results match!


1.3.3 Where - Simple 3
This query selects items from the product list where the number of items in stock is non-zero and the item's
unit price is greater than 3.00.

Page 16 of 69
The results match; they are produced by the following function:

’ Z„Linq3;L;R;Linq;APL;products
[1] © RESTRICTION OPERATOR: Where - Simple 3
[2] Linq„Œwi 'Linq3'
[3] products„Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4] L„¹(Œio+3)œ¨products © Fourth element is UnitPrice
[5] R„¹¯1†¨products © Last (fifth) element is UnitsInStock
[6] APL„(Œio+1)œ¨((R>0)^L>3)/products
[7] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

I am using a screenshot to capture the results of functions, especially where the results are verbose, as in
this instance. This is purely a convenience for formatting the content of this document. For your exploration,
you may choose to stop the functions after the penultimate line and then examine or debug the individual
components of the results at your leisure; the last line shows the complete results.

How does it work? With C# a two-dimensional string array, potentially, each row has a different length: the
length of the number of characters in it. With APL+Win, all rows have the same length—they are padded with
trailing space making up a length equal to the longest string in the array. However, with a nested string vector,
each element occupies only enough space to accommodate its own length irrespective of the length of co-
existing elements; this feature simplifies string relational comparisons.
1.3.4 Where - Drilldown
This query prints a list of customers from the state of Washington along with their orders. The list of customers
holds customers details and their orders.

Linq4
LINQ LAZYK Lazy K Kountry Store 10482 21/03/1997
10545 22/05/1997
TRAIH Trail's Head Gourmet Provisioners 10574 19/06/1997
10577 23/06/1997
10822 08/01/1998
WHITC White Clover Markets 10269 31/07/1996
10344 01/11/1996
10469 10/03/1997
10483 24/03/1997
10504 11/04/1997
10596 11/07/1997
10693 06/10/1997
10696 08/10/1997
10723 30/10/1997
10740 13/11/1997
10861 30/01/1998
10904 24/02/1998
11032 17/04/1998
11066 01/05/1998
APL LAZYK Lazy K Kountry Store 10482 21/03/1997
10545 22/05/1997
TRAIH Trail's Head Gourmet Provisioners 10574 19/06/1997
10577 23/06/1997
10822 08/01/1998
WHITC White Clover Markets 10269 31/07/1996
10344 01/11/1996
10469 10/03/1997
10483 24/03/1997
10504 11/04/1997
10596 11/07/1997
10693 06/10/1997
10696 08/10/1997
10723 30/10/1997
10740 13/11/1997

Page 17 of 69
10861 30/01/1998
10904 24/02/1998
11032 17/04/1998
11066 01/05/1998
Match? 1

The function is defined as follows:

’ Z„Linq4;Linq;APL
[1] © RESTRICTION OPERATOR: Where - DrillDown
[2] Linq„œœ¨¨Œwi 'Linq4'
[3] APL„Œwi 'GetCustomerListEx' © CustomerID 1,CompanyName
2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone 8,Fax 9,Orders
10
[4] APL„(((Œio+4)œ¨APL)¹›'WA')/APL © Just those in the WA Region -
<Region> is the fifth element
[5] APL„(Œio+0 1 9)œ¨¨›APL © <CustomerID>, <CustomerName> &
<Orders> i.e. First, Second & Tenth element
[6] ((Œio+2)œAPL)„œ¨1 1 0/¨¨(Œio+2)œAPL © <OrderId> & <OrderDate> i.e First
& Second element of <Orders>, now the third element
[7] APL„³œAPL
[8] Z„('LINQ' 'APL' 'Match?'),[Œio+0.5] Linq APL (Linq−APL)

The comments in this function provide valuable clues on the computation of the result. If the logic is not
self-evident, you need to examine the function line by line.

The COM method GetCustomerListEx hides a subtlety: because APL+Win does not support the datetime
data type, the method returns the date information as a string, formatted in short date regional format. I am
using the UK regional format, that is, dd/mm/yyyy15. For reporting purposes, this technique is adequate, as it
does not involve any date arithmetic.
1.3.5 Where - Indexed
This query uses an indexed Where clause to print the name of each number, from 0-9, where the length of the
number's name is shorter than its value. In this case, the code is passing a lamda expression which is
converted to the appropriate delegate type. The body of the lambda expression tests whether the length of the
string is less than its index in the array.

At this point, the reality of the C# jargon hits home; as mentioned, it is beyond the scope of the present
objective. Briefly, a lambda expression is an anonymous (one without a name) function and contains
statements like any function. For the lambda operator => read “goes to”. A good reference on C# 3.0 is a
prime requisite for understanding the Linq to Objects samples: at the very least you need to read the words
accompanying each example at the URL http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx.

Linq5
LINQ five six seven eight nine
APL five six seven eight nine
Match? 1

The function is defined as follows:

’ Z„Linq5;Linq;APL
[1] © RESTRICTION OPERATOR: Where - Indexed
[2] Linq„Œwi 'Linq5'

15
Depending on your locale, you may see different results in your session; however, the C# and APL+Win results will both be consistent with your
locale.

Page 18 of 69
[3] APL„"zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine"
[4] APL„(¹(½¨APL)<(-Œio)+¼½APL)/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.4 Projection Operators
Projection operators transform their argument into new objects, the results; the arguments and results may be
of different types. These operators often involve calculation within a selection.
1.4.1 Select - Simple 1
This query returns a sequence of integers one greater than those in an input array; it uses the expression in
the select clause to add one to each element in the new sequence. The C# code is:

public int[] Linq6()


{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numsPlusOne = from n in numbers
select n + 1;
return numsPlusOne.ToArray();
}

APL+Win does not support the facility to select a number and perform an arithmetic operation on that
number in one expression. However, APL+Win can produce the same result.

Linq6
LINQ 6 5 2 4 10 9 7 8 3 1
APL 6 5 2 4 10 9 7 8 3 1
Match? 1

The APL function is defined as follows:

’ Z„Linq6;Linq;APL
[1] © Projection Operators - Select Simple 1
[2] Linq„Œwi 'Linq6'
[3] APL„1+ 5 4 1 3 9 8 6 7 2 0
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.4.1.1 Linq6 in focus
The alternative or conventional C# way of achieving the same result is:

public int[] Linq6alt()


{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int[] res = new int[numbers.Length];
for (int i = 0;i< numbers.Length;i++)
{
res[i]=numbers[i]+0;
}
return res;
}

It is now apparent that this LINQ query simply saves the developer two things: first, an explicit loop and
second the facility to return a subset of the argument vector by the use of a where clause, if necessary.

In fact, Linq6 hides a much more important subtlety: if, say, only even numbers are retained from the
argument, the alternative solution will not work since it is impossible to determine how many elements are
even without executing another loop to determine it first. Then, the solution to a simple problem is at risk of
becoming a clutter of code.

Page 19 of 69
16
Of course, the APL solution takes such a restriction in its stride :

1+(0=2|APL)/APL„5 4 1 3 9 8 6 7 2 0
5 9 7 3 1
1.4.2 Select - Simple 2

This query returns the name of every product in the product list. The matching APL+Win result comes from
the following function:

’ Z„Linq7;Linq;APL
[1] © RESTRICTION OPERATOR: Where - Indexed
[2] Linq„œŒwi 'Linq7'
[3] APL„œ(Œio+1)œ¨Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock. ProductName is second element
[4] Z„•(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

1.4.3 Select - Transformation
This query returns the name of each number in an integer array by indexing into a second array that contains
the names. The C# solution works because it works in index origin zero.

APL+Win can return the same result in index origin one or zero; the result drawn from the C# DLL work in
the only index origin possible, zero, irrespective of the setting in APL+Win.

Œio„1 ª Linq8
LINQ five four one three nine eight six seven two zero
APL five four one three nine eight six seven two zero
Match? 1

Œio„0 ª Linq8
LINQ five four one three nine eight six seven two zero

16
Unlike C#, APL+Win can resize/reorder arrays dynamically.

Page 20 of 69
APL five four one three nine eight six seven two zero
Match? 1

The APL+Win function is defined as follows:

’ Z„Linq8;Linq;APL
[1] © Projection Operators: Select - Transformation
[2] Linq„Œwi 'Linq8'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„("zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine" )[APL+Œio]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.4.4 Select - Anonymous Types 1
This query returns each element of its argument in uppercase and lowercase. With APL+Win, I will not use the
CharUpper or CharLower Application Programming Interface (API) calls in order to keep the comparison
strictly at the language level.

The solution is quite simple: substitute each of the 26 characters with one in the corresponding case.

’ R„L LCase R
[1] © Return R in uppercase
[2] L„Œio
[3] :for i :in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
[4] ((i=,R)/,R)„'abcdefghijklmnopqrstuvwxyz'[L]
[5] L„L+1
[6] :endfor

’ R„L UCase R
[1] © Return R in uppercase
[2] L„Œio
[3] :for i :in 'abcdefghijklmnopqrstuvwxyz'
[4] ((i=,R)/,R)„'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[L]
[5] L„L+1
[6] :endfor

The following APL+Win function produces a comparison of the solution:

’ Z„Linq9;Linq;APL
[1] © Projection Operators - Select - Anonymous Type 1
[2] Linq„Œwi 'Linq9'
[3] APL„œ,¨/›¨¨(UCase ¨"aPPLE" "BlUeBeRrY" "cHeRry") (LCase ¨"aPPLE"
"BlUeBeRrY" "cHeRry")
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The results match:

Linq9
LINQ APPLE apple BLUEBERRY blueberry CHERRY cherry
APL APPLE apple BLUEBERRY blueberry CHERRY cherry
Match? 1
1.4.5 Select - Anonymous Types 2
This query iterates over the elements of an input integer vector and returns the element’s name and parity,
that is, whether it is odd or even. It works because string vector holds the digit names in numerical sequence,
starting with zero—the index origin in C#.

Page 21 of 69
The APL+Win function—index origin independent—is:

’ Z„Linq10;Linq;APL
[1] © Projection Operators: Select - Anonymous Types 2
[2] Linq„Œwi 'Linq10'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„(›¨("zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine")[Œio+APL]),¨ ›¨('even' 'odd')[Œio+2|APL]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.4.6 Select - Anonymous Types 3
This query returns the name of every product in the product list along with the category of the product and its
unit price. The C# version renames UnitPrice to Price. As mentioned, APL+Win identifies elements of a
nested array by its cardinal order and not by name.

The APL+Win function that produces a matching result:

’ Z„Linq11;Linq;APL
[1] © Projections Operators: Select - Anonymous Types 3
[2] Linq„œŒwi 'Linq11'
[3] APL„0 1 1 1 0/œŒwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[5] …0
[6] (ProductName Category Price)„,›[Œio+1]³APL

Note line [6]: APL+Win can easily reclaim the names of the columns in the result: C# retains the names
intrinsically and APL+Win assigns them arbitrarily.
1.4.7 Select - Indexed
This query returns the value of each element in a vector of integers whether the element matches its index—
in index origin zero—in the vector. The index of each element is inferred rather than index in another vector
holding the actual indices.

Linq12
LINQ APL Match?

5 0 5 0 1
4 0 4 0
1 0 1 0
3 1 3 1
9 0 9 0
8 0 8 0

Page 22 of 69
6 1 6 1
7 1 7 1
2 0 2 0
0 0 0 0

Although the results match, there is a deviation in the result: 1 and 0 are shown instead of ‘true’ and
‘false’, respectively. The APL+Win function:

’ Z„Linq12;Linq;APL
[1] © Projections Operators: Select Indexed
[2] Linq„³œŒwi 'Linq12'
[3] APL„³œAPL (APL=(-Œio)+¼½APL„5 4 1 3 9 8 6 7 2 0)
[4] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.4.8 Select - Filtered
This query prints the name of each element of an integer vector that is less than five; it combines ‘select’ and
‘where’ to build a simple query that indexes into another string vector that contains the names of the elements.
All elements are less than nine.

Linq13
LINQ APL Match?

four four 1
one one
three three
two two
zero zero

The function is:

’ Z„Linq13;Linq;APL
[1] © Projection Operators: Select - Filtered
[2] Linq„œŒwi 'Linq13'
[3] APL„"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
[4] APL„œAPL[Œio+(5 4 1 3 9 8 6 7 2 0<5)/5 4 1 3 9 8 6 7 2 0]
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.4.9 SelectMany - Compound from 1
This example identifies ordered pairs of integers, such that the first number is an element of one integer
vector and the second number is an element of another integer vector where the first number is less than the
second number. The query uses a compound ‘from’ clause to compose a query that returns a sequence of the
pairs.

’ Z„Linq14;Linq;APL;numbersA;numbersB;Select
[1] © Projection Operators: SelectMany - Compound from 1
[2] Linq„³œŒwi 'Linq14'
[3] numbersA„0 2 4 5 6 8 9
[4] numbersB„1 3 5 7 8
[5] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[6] APL„(›[Œio+1]numbersA°.<numbersB) Select ¨›numbersB
[7] APL„numbersA,¨¨APL
[8] APL„³†,/³¨œ¨(¹×½¨APL)/APL
[9] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

The results match:

LINQ APL Match? This query is more complicated than it seems at first sight.

0 1 0 1 1 • Elements of the first vector, numbersA, are replicated as many

Page 23 of 69
0 3 0 3 times as there are elements in the second vector, numbersB.
0 5 0 5
0 7 0 7 • The fact that both vectors are sorted in ascending order is a
0 8 0 8 coincidence; the order is inconsequential.
2 3 2 3
2 5 2 5 • Elements from the first vector that are greater than all the
2 7 2 7 elements of the second vector are discarded.
2 8 2 8
4 5 4 5 Note the localised function, ‘Select’, that is used by the APL solution. In
4 7 4 7 order to explore the reason why this is necessary, replace ‘Select’ by ‘/’
4 8 4 8 on line [6] and investigate.
5 7 5 7
5 8 5 8 Read the result row by row: the first number is less than the second
6 7 6 7 number.
6 8 6 8

1.4.10 SelectMany - Compound from 2


This query returns all orders whose total is less that 500; each such order is identified by the CustomerID and
OrderID. The list of customers holds each CustomerID uniquely but the ‘orders’ details are recurring.

APL+Win solution is:

’ Z„Linq15;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: SelectMany - Compound from 2
[2] Linq„œŒwi 'Linq15'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] CustomerID„Œioœ¨APL © CustomerID
[6] Orders„(››1 0 1) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderDate 2
[7] APL„(¹¨500>¯1†¨¨Orders) Select ¨Orders © Order Values
below 500
[8] (CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[9] APL„œ¨(›¨›¨CustomerID),¨¨APL © Add CustomerID
[10] APL„œ,[Œio]/APL © Return as a 3
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

The results match. The function Select is localised for the same reasons hinted in the previous example.
You did investigate why this is necessary, didn’t you?
1.4.11 SelectMany - Compound from 3
This query returns all orders made in 1998 or later.

Page 24 of 69
The APL+Win function that produces the matching result is:

’ Z„Linq16;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: SelectMany - Compound from 3
[2] Linq„œŒwi 'Linq16'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] CustomerID„Œioœ¨APL © CustomerID
[6] Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderValue 3
[7] APL„(¯1†¨¨Orders) © Extract the Order
Date UK Short date format DD/MM/YYYY
[8] APL„100ƒ¨¨¨,¨¨¨´¨¨¨(›››100 100 10000)‚¨¨¨Œfi¨¨¨APL~¨¨¨'/' © Convert to
ISO format YYYYMMDD
[9] APL„¹¨APL‰19980101 © Selection vector
[10] APL„APL Select ¨Orders © Orders on or
after 1 January 1998
[11] (CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[12] APL„œ¨(›¨›¨CustomerID),¨¨APL © Add CustomerID
[13] APL„œ,[Œio]/APL © Return as a 3
column array
[14] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[15] …0
[16] This involves selection by date, a data type that APL+Win does not
support inherently
[17] As this involves a simple date comparison, it is easy to convert the
date to ISO format & use the results as integers

The LINQ query specifies all orders made in 1998: for APL+Win, I have interpreted this as meaning on or
after first of January 1998. This is necessary because APL+Win lacks the date data type and the supporting
functions that return the constituents of a date value such as year, month, etc. Refer to lines [16]-[17] for other
details. Although it is quite feasible to write the C# code to return the year for this example, I felt that it was
better to keep the function GetCustomerListEx generic—it returns the same result irrespective of the example
where it is used.
1.4.12 SelectMany - from Assignment
This query returns all orders where the order total is greater than 2000.00.

Page 25 of 69
The APL+Win function that returns the result is:

’ Z„Linq17;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: SelectMany - from Assignment
[2] Linq„œŒwi 'Linq17'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] CustomerID„Œioœ¨APL © CustomerID
[6] Orders„(››1 0 1) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderDate 2
[7] APL„(¹¨2000<¯1†¨¨Orders) Select ¨Orders © Order Values
below 500
[8] (CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[9] APL„œ¨(›¨›¨CustomerID),¨¨APL © Add CustomerID
[10] APL„œ,[Œio]/APL © Return as a 3
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[12] …0
[13] How is the query paradigm different from Linq15? I've included it here
for completeness.

Note my comment on line [13].


1.4.13 SelectMany - Multiple from
The LINQ documentation for this example reads as follows: “This sample uses multiple from clauses so that
filtering on customers can be done before selecting their orders. This makes the query more efficient by not
selecting and then discarding orders for customers outside of Washington.” In other words, customers from
within Washington are selected first and then their orders.

The APL+Win function is:

’ Z„Linq18;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: SelectMany - Multiple from
[2] Linq„œŒwi 'Linq18'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] APL„(((Œio+4)œ¨APL)¹›'WA')/APL © Just those in the
WA Region - <Region> is the fifth element
[6] CustomerID„Œioœ¨APL © CustomerID
[7] Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) without OrderValue 3
[8] APL„(¯1†¨¨Orders) © Extract the Order
Date UK Short date format DD/MM/YYYY

Page 26 of 69
[9] APL„100ƒ¨¨¨,¨¨¨´¨¨¨(›››100 100 10000)‚¨¨¨Œfi¨¨¨APL~¨¨¨'/' © Convert to
ISO format YYYYMMDD
[10] APL„¹¨APL‰19970101 © Selection vector
[11] APL„APL Select ¨Orders © Orders on or
after 1 January 1998
[12] (CustomerID APL)„(›×†¨½¨APL) Select ¨CustomerID APL © Remove those
without qualifying OrderValue
[13] APL„(››1 0) Select ¨¨APL © Drop OrderValue
[14] APL„œ¨(›¨›¨CustomerID),¨¨APL © Add CustomerID
[15] APL„œ,[Œio]/APL © Return as a 2
column array
[16] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)
[17] …0
[18] This involves selection by date, a data type that APL+Win does not
support inherently
[19] As this involves a simple date comparison, it is easy to convert the
date to ISO format & use the results as integers

The results match:

Linq18
LINQ APL Match?
LAZYK 10482 LAZYK 10482 1
LAZYK 10545 LAZYK 10545
TRAIH 10574 TRAIH 10574
TRAIH 10577 TRAIH 10577
TRAIH 10822 TRAIH 10822
WHITC 10469 WHITC 10469
WHITC 10483 WHITC 10483
WHITC 10504 WHITC 10504
WHITC 10596 WHITC 10596
WHITC 10693 WHITC 10693
WHITC 10696 WHITC 10696
WHITC 10723 WHITC 10723
WHITC 10740 WHITC 10740
WHITC 10861 WHITC 10861
WHITC 10904 WHITC 10904
WHITC 11032 WHITC 11032
WHITC 11066 WHITC 11066

A simple reminder: short results are shown within the body of the document as text and results that are
long are shown partially as a screenshot—you are invited to run the functions to see the results in full. Also,
this document is best viewed with magnification, say 150%, as this renders the screenshots bigger and hence
readable with greater ease.
1.4.14 SelectMany - Indexed
This query returns the CustomerID and OrderID for every order in the database—the Customer list. This query
is interesting in that it returns the results as formatted text. What if the raw results are required?

Page 27 of 69
Superficially, the results do not match! However, if you examine the results more closely, C# returns the
same result as APL+Win; the difference is in the format. The APL+Win function is:

’ Z„Linq19;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: SelectMany - Indexed
[2] Linq„œŒwi 'Linq19'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] CustomerID„Œioœ¨APL © CustomerID
[6] CustomerID„›¨,¨+\(½CustomerID)/1 © Give each
customer it own cardinal number
[7] Orders„(››1 0 0) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID only
[8] (CustomerID Orders)„(›0¬¹½¨Orders) Select ¨CustomerID Orders © Remove
those without any Orders
[9] APL„œ¨(›¨›¨CustomerID),¨¨Orders © Add CustomerID
[10] APL„œ,[Œio]/APL © Return as a 2
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5 Partitioning Operators
This group of operators splice their arguments and return subset as their result
1.5.1 Take - Simple
This query uses ‘Take’ to generate a sequence of the first three elements of an integer vector; note that in this
instance, not only do C# LINQ and APL+Win share the jargon but the jargon has an identical meaning in both
contexts.

’ Z„Linq20;Linq;APL
[1] © Partitioning Operators: Take - Simple
[2] Linq„œŒwi 'Linq20'
[3] APL„3†5 4 1 3 9 8 6 7 2 0
[4] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Of course, the results match!

Linq20
LINQ APL Match?
5 4 1 5 4 1 1
1.5.2 Take - Nested
This query returns customer ID, order ID, and order date for the first three orders from customers in
Washington. The sample uses ‘Take’ to limit the sequence generated by the query expression to the first
three of the orders. The hidden complication is that some customers may have zero orders.

Linq21
LINQ APL Match?
LAZYK 10482 21/03/1997 LAZYK 10482 21/03/1997 1
LAZYK 10545 22/05/1997 LAZYK 10545 22/05/1997
TRAIH 10574 19/06/1997 TRAIH 10574 19/06/1997

The function is:

’ Z„Linq21;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: Take - Nested
[2] Linq„œŒwi 'Linq21'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'

Page 28 of 69
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] APL„(((Œio+4)œ¨APL)¹›'WA')/APL © Just those in the
WA Region - <Region> is the fifth element
[6] CustomerID„Œioœ¨APL © CustomerID
[7] Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID, OrderDate only
[8] (CustomerID Orders)„(›0¬¹½¨Orders) Select ¨CustomerID Orders © Remove
those without any Orders
[9] APL„œ¨(›¨›¨CustomerID),¨¨Orders © Add CustomerID
[10] APL„œ,[Œio]/APL © Return as a 3
column array
[11] APL„œ3†›[Œio+1]APL
[12] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

The APL+Win can compute other variations in this query that may also hide complications.
1.5.2.1 First two customers in Washington and all their orders
Assuming that there are more than two customers with orders in Washington:

[9.5] APL„2†APL
[11] © APL„œ3†›[Œio+1]APL
1.5.2.2 First 3 customers in Washington and their first 5 orders
Assuming that there are more than three customers in Washington with orders: note that some customers
may have fewer than 5 orders.

[9.5] APL„œ,[Œio]/((3,¨1‡¨ ½¨APL)˜½¨APL)†¨APL © First 3 orders of the first


3 customers in Washington APL
[11] © APL„œ3†›[Œio+1]APL

The interesting challenge is in coding C# for these two variations!


1.5.3 Skip - Simple
This query uses ‘Skip’ to get all but the first 4 elements of the array.

Linq22
LINQ APL Match?
9 8 6 7 2 0 9 8 6 7 2 0 1
The APL+Win solution:

’ Z„Linq22;Linq;APL
[1] © Partitioning Operators: Skip - Simple
[2] Linq„œŒwi 'Linq22'
[3] APL„4‡5 4 1 3 9 8 6 7 2 0
[4] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

If there are fewer than four elements in the argument, the C# and APL+Win solutions return matching
results; consider this as an exercise!
1.5.4 Skip - Nested
This query returns all but the first two orders from customers in Washington.

’ Z„Linq23;Linq;APL;Select;CustomerID;Orders
[1] © Projection Operators: Skip - Nested
[2] Linq„œŒwi 'Linq23'
[3] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'

Page 29 of 69
[4] APL„Œwi 'GetCustomerListEx' © CustomerID
1,CompanyName 2,Address 3,City 4,Region 5,PostalCode 6,Country 7,Phone
8,Fax 9,Orders 10
[5] APL„(((Œio+4)œ¨APL)¹›'WA')/APL © Just those in the
WA Region - <Region> is the fifth element
[6] CustomerID„Œioœ¨APL © CustomerID
[7] Orders„(››1 1 0) Select ¨¨(Œio+9)œ¨APL © Orders(OrderID 1,
OrderDate 2, OrderValue 3) Keep OrderID, OrderDate only
[8] (CustomerID Orders)„(›0¬¹½¨Orders) Select ¨CustomerID Orders © Remove
those without any Orders
[9] APL„œ¨(›¨›¨CustomerID),¨¨Orders © Add CustomerID
[10] APL„2 0‡œ,[Œio]/APL © Return as a 3
column array
[11] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Linq23
LINQ APL Match?
TRAIH 10574 19/06/1997 TRAIH 10574 19/06/1997 1
TRAIH 10577 23/06/1997 TRAIH 10577 23/06/1997
TRAIH 10822 08/01/1998 TRAIH 10822 08/01/1998
WHITC 10269 31/07/1996 WHITC 10269 31/07/1996
WHITC 10344 01/11/1996 WHITC 10344 01/11/1996
WHITC 10469 10/03/1997 WHITC 10469 10/03/1997
WHITC 10483 24/03/1997 WHITC 10483 24/03/1997
WHITC 10504 11/04/1997 WHITC 10504 11/04/1997
WHITC 10596 11/07/1997 WHITC 10596 11/07/1997
WHITC 10693 06/10/1997 WHITC 10693 06/10/1997
WHITC 10696 08/10/1997 WHITC 10696 08/10/1997
WHITC 10723 30/10/1997 WHITC 10723 30/10/1997
WHITC 10740 13/11/1997 WHITC 10740 13/11/1997
WHITC 10861 30/01/1998 WHITC 10861 30/01/1998
WHITC 10904 24/02/1998 WHITC 10904 24/02/1998
WHITC 11032 17/04/1998 WHITC 11032 17/04/1998
WHITC 11066 01/05/1998 WHITC 11066 01/05/1998
1.5.5 TakeWhile - Simple
This query uses ‘TakeWhile’ to generate return all elements from a numeric vector until elements are less than
6. From an APL+Win point of view, this query is trivial.

Linq24
LINQ APL Match?
5 4 1 3 5 4 1 3 1

’ Z„Linq24;Linq;APL
[1] © Partitioning Operators: TakeWhile - Simple
[2] Linq„œŒwi 'Linq24'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„(^\APL<6)/APL
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5.6 TakeWhile - Indexed17
This query uses ‘TakeWhile’ to return elements of a numeric vector, starting from the beginning of the vector
until a number is hit that is less than its position in the vector: remember that C# works in index origin 0.

17
This is missing in the links, shown at the start, at http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx

Page 30 of 69
LINQ APL Match?
5 4 5 4 1

’ Z„Linq25;Linq;APL
[1] © Partitioning Operators: TakeWhile - Indexed
[2] Linq„œŒwi 'Linq25'
[3] APL„(^\APL>(APL¼APL)-Œio)/APL„5 4 1 3 9 8 6 7 2 0
[4] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5.7 SkipWhile - Simple
This query returns all of the elements of an integer vector, skipping elements until one of them is divisible by
three.

LINQ APL Match?


3 9 8 6 7 2 0 3 9 8 6 7 2 0 1

’ Z„Linq26;Linq;APL
[1] © Partitioning Operators: SkipWhile - Simple
[2] Linq„œŒwi 'Linq26'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„(Ÿ\0=3|APL)/APL
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.5.8 SkipWhile - Indexed
This query returns all of the elements of an integer vector, skipping elements until the element's value is less
that its position in the array: remember that C# works in index origin 0.

Linq27
LINQ APL Match?
1 3 9 8 6 7 2 0 1 3 9 8 6 7 2 0 1

’ Z„Linq27;Linq;APL
[1] © Partitioning Operators: SkipWhile - Indexed
[2] Linq„œŒwi 'Linq27'
[3] APL„(Ÿ\APL<(-Œio)+¼½APL)/APL„5 4 1 3 9 8 6 7 2 0
[4] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6 Ordering Operators
This group of operators re-order or sort their arguments according to different criteria. They include simple,
even trivial, examples that illustrate principles. Further refinement to the techniques is necessary to build re-
usable code that incorporates the same techniques.
1.6.1 OrderBy - Simple 1
This query demonstrates a simple alphabetical sort. Although the example does not demonstrate this, the
sorting is case insensitive: I have added a word in upper case to the argument to demonstrate.

Linq28
LINQ APL Match?

apple apple 1
ASKOOLUM ASKOOLUM
blueberry blueberry
cherry cherry

’ Z„Linq28;Linq;APL
[1] © Ordering Operators: OrderBy - Simple 1
[2] Linq„œŒwi 'Linq28'
[3] APL„"cherry" "apple" "blueberry" "ASKOOLUM" © Case insensitive
[4] APL„œAPL[“Œav¼œLCase ¨APL]

Page 31 of 69
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6.2 OrderBy - Simple 2
This query returns its vector argument sorted in ascending order of the length of each element.

Linq29
LINQ APL Match?

apple apple 1
cherry cherry
blueberry blueberry

The function:

’ Z„Linq29;Linq;APL
[1] © Ordering Operators: OrderBy - Simple 2
[2] Linq„œŒwi 'Linq29'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„œAPL[”' '+.=³œAPL]
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6.3 OrderBy - Simple 3
This query returns all of the products sorted alphabetically by the product name.

This result is interesting: it does not match!

’ Z„Linq30;Linq;APL
[1] © Ordering Operators: OrderBy - Simple 3
[2] Linq„œŒwi 'Linq30'
[3] APL„Œwi 'GetProductListEx' © ProductID ProductName Category UnitPrice
UnitsInStock
[4] APL„(œAPL)[“Œav¼œ(Œio+1)œ¨APL;]
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6.3.1 Where is the difference?
On stopping the function after line [4] and running a query to isolate the differences yields:

Page 32 of 69
The results match in terms of the number of rows. The difference lies with the character data. C# sorts the
results and passes it to APL+Win; it also passes the raw data to APL+Win. There is loss of accuracy arising
from the lack of support for Unicode in APL+Win; in contrast, C# uses Unicode. APL+Win is sorting the data
after the loss of accuracy whereas it is the C# result—not the data—that suffers the same loss.
1.6.4 OrderBy - Comparer
This example returns a string vector sorted according to a case insensitive alphanumeric sort.

Linq31
LINQ AbAcUs aPPLE BlUeBeRrY bRaNcH cHeRry ClOvEr
APL AbAcUs aPPLE BlUeBeRrY bRaNcH cHeRry ClOvEr
Match? 1

The APL+Win function:

’ Z„Linq31;Linq;APL
[1] © Ordering Operators - OrderBy - Comparer
[2] Linq„Œwi 'Linq31'
[3] APL„"aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr" "cHeRry"
[4] APL„APL[“Œav¼UCase œAPL]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

In order to keep the results comparable, I have not used any API calls in the APL+Win solution.
1.6.5 OrderByDescending - Simple 1
This query returns a vector of floating point numbers sorted in descending order.

Linq32
LINQ 4.1 2.9 2.3 1.9 1.7
APL 4.1 2.9 2.3 1.9 1.7
Match? 1.0

The function:

’ Z„Linq32;Linq;APL
[1] © Ordering Operators - OrderByDescending - Simple 1
[2] Linq„Œwi 'Linq32'
[3] APL„1.7 2.3 1.9 4.1 2.9
[4] APL„APL[”APL]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.6.6 OrderByDescending - Simple 2
This query returns a list of products sorted by the number of units of each product that are in stock.

Page 33 of 69
The APL+Win function:

’ Z„Linq33;Linq;APL
[1] © Ordering Operators - OrderByDescending - Simple 2
[2] Linq„œŒwi 'Linq33'
[3] APL„Œwi 'GetProductListEx'
[4] APL„œAPL[”(Œio+4)œ¨Œwi 'GetProductListEx'] © ProductID ProductName
Category UnitPrice UnitsInStock
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.6.7 OrderByDescending - Comparer
This query returns a vector of strings sorted alphabetically in descending order, using a case insensitive
comparison.

Linq34
LINQ ClOvEr cHeRry bRaNcH BlUeBeRrY aPPLE AbAcUs
APL ClOvEr cHeRry bRaNcH BlUeBeRrY aPPLE AbAcUs
Match? 1

The APL+Win function:

’ Z„Linq34;Linq;APL
[1] © Ordering Operators - OrderByDescending - Comparer
[2] Linq„Œwi 'Linq34'
[3] APL„"aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr" "cHeRry"
[4] APL„APL[”Œav¼LCase œAPL]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.6.8 ThenBy - Simple
This sample uses a compound ‘orderby’ to sort a list of digits first by length of their name, and then
alphabetically.

Linq35
LINQ one six two five four nine zero eight seven three
APL one six two five four nine zero eight seven three
Match? 1

’ Z„Linq35;Linq;APL
[1] © Ordering Operators - ThenBy - Simple
[2] Linq„Œwi 'Linq35'
[3] APL„"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
[4] APL„APL[“Œav¼LCase œAPL]
[5] APL„APL[“¹½¨APL]
[6] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Page 34 of 69
1.6.9 ThenBy - Comparer
This sample prints an array of string values sorted first by length, then sorted alphabetically, using a case-
insensitive comparison.

’ Z„Linq36 words;Linq;APL
[1] © Ordering Operators - ThenBy - Comparer
[2] Linq„Œwi 'Linq36' words
[3] words„words[“Œav¼LCaseœwords]
[4] APL„words[“¹½¨words]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
[6] …0
[7] Linq36 "aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr" "cHeRry"
[8] Linq36 "zero" "oNe" "TWo" "tHRee" "foUR" "FIVE" "sIx" "SEVEn"
"eigHt" "NIne" "TEn" "HundRED"

1.6.10 ThenByDescending - Simple
This sample uses a compound ‘orderby’ to sort a list of products, first by category in ascending order, and
then by unit price in descending order.

’ Z„Linq37;Linq;APL
[1] © Ordering Operators - ThenByDescending - Simple
[2] Linq„œŒwi 'Linq37'
[3] APL„Œwi 'GetProductListEx'
[4] APL„œAPL[(”(Œio+3)œ¨APL)[“Œav¼œ ((Œio+2)œ¨APL) [”(Œio+3)œ¨APL]]]
[5] Z„(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)
[6] …0
[7] © Take it a little slower!
[8] APL„Œwi 'GetProductListEx' © Get the Product List
[9] UnitPrice„(Œio+3)œ¨APL © UnitProce is the 4th element
[10] Category„(Œio+2)œ¨APL © Category is the 3rd element
[11] APL„œAPL[(”UnitPrice)[“Œav¼œCategory[”UnitPrice]]]

1.6.11 ThenByDescending - Comparer
This sample uses an ‘OrderBy’ and a ‘ThenBy’ clause with a custom comparer to sort first by word length and
then by a case-insensitive descending sort of the words in an array.

Page 35 of 69
’ Z„Linq38 words;Linq;APL
[1] © Ordering Operators: ThenByDescending - Comparer
[2] Linq„Œwi 'Linq38' words
[3] words„words[“Œav¼LCase œwords]
[4] APL„words[²”¹½¨words]
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
[6] …0
[7] Linq38 "aPPLE" "AbAcUs" "bRaNcH" "BlUeBeRrY" "ClOvEr" "cHeRry"
[8] Linq36 "zero" "oNe" "TWo" "tHRee" "foUR" "FIVE" "sIx" "SEVEn"
"eigHt" "NIne" "TEn" "HundRED"

1.6.12 Reverse
This query returns a list strings from an input string vector where each element has the second letter 'i'. The
output list the elements in reverse order. The sample uses a query operator to gather the elements that have
'i' as the second letter and then reverses the sequence using the Reverse method.

Linq39
LINQ nine eight six five
APL nine eight six five
Match? 1

’ Z„Linq39;Linq;APL
[1] © Ordering Operators: ThenByDescending - Comparer
[2] words„"zero" "one" "two" "three" "four" "five" "six" "seven"
"eight" "nine"
[3] Linq„Œwi 'Linq39' words
[4] APL„²(¹'i'=1†¨1‡¨words)/words
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
[6] …0
[7] APL„²(¹'i'Ÿ.¹¨words)/words © to pick up words with at least one 'i'
anywhere
[8] APL„²(¹'io'Ÿ.º¨words)/words © to pick up words with at least one
substring 'io' anywhere

1.7 Grouping Operators
This group of operators collates elements contiguously depending on applicable criteria.
1.7.1 GroupBy - Simple 1
This query uses group by to partition a list of numbers by their remainder when divided by 5.

Linq40
LINQ APL Match?

5 0 5 0 1
0 0 0 0
4 4 4 4
9 4 9 4
1 1 1 1
6 1 6 1

Page 36 of 69
3 3 3 3
8 3 8 3
7 2 7 2
2 2 2 2

The following function produces the matching result:

’ Z„Linq40;L;R;Linq;APL
[1] © Grouping Operators: GroupBy - Simple 1
[2] Linq„³Œwi 'Linq40'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] L„5|APL
[5] R„((L¼L)=¼½L)/L
[6] (APL L)„œ¨(›“R¼L)Þ¨¨›¨L APL
[7] APL„³œL APL
[8] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.7.2 GroupBy - Simple 2
This sample partitions a vector of words into groups according to the first letter of each word.

Linq41
LINQ APL Match?
b blueberry b blueberry 1
b banana b banana
c chimpanzee c chimpanzee
c cheese c cheese
a abacus a abacus
a apple a apple

’ Z„Linq41;L;R;Linq;APL
[1] © Grouping Operators: GroupBy - Simple 2
[2] Linq„³Œwi 'Linq41'
[3] APL„"blueberry" "chimpanzee" "abacus" "banana" "apple" "cheese"
[4] L„1†¨APL
[5] R„((L¼L)=¼½L)/L
[6] (L APL)„ œ¨(›“R¼L)Þ¨¨›¨L APL
[7] APL„³œL APL
[8] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.7.3 GroupBy - Simple 3
This sample uses group by to partition a list of products by category. Coding the C# method to return the
result to APL+Win was nothing short of an unpleasant ordeal, given the simplicity of the query.

The APL function:

’ Z„Linq42;Linq;APL;Category;SelectByCategory;Select
[1] © Grouping Operators - Simple 3
[2] Linq„Œwi 'Linq42' © œœ¨¨Œwi 'Linq42'
[3] APL„Œwi 'GetProductListEx'
[4] 0 0½Œdef '’ Z„L Select R',Œtcnl,'[1] Z„L/R ’'
[5] Category„(Œio+2)œ¨APL
[6] DistinctCategory„((Category¼Category)=¼½Category)/Category
[7] SelectByCategory„›[Œio+1](DistinctCategory)°.−Category
[8] APL„((›¨DistinctCategory),¨›¨SelectByCategory Select ¨›APL)
[9] Z„(œœ¨¨Linq) (œœ¨¨APL)
[10] Z„(›¨'APL' 'Linq' 'Match?'),[Œio-0.5]Z ,›(Linq−APL)

Page 37 of 69
You will need to run the query in order to see the detail of the result. As an aside, this illustrates the
possibilities of created nested variables in C# in ways that make them compatible with ones in APL+Win.
1.7.4 GroupBy - Nested

1.7.5 GroupBy – Comparer


This query and the next one delivers the same functionality as the next one except in one respect: it does not
convert the result to uppercase. It also suffers from the same deficiencies

Linq44
LINQ APL Match?
from from FORM 0
salt last salt last
earn near earn near
FORM

The comparison fails since I have corrected the anomalies of the C# solution when coding the APL+Win
function:

’ Z„Linq44;Linq;APL
[1] © Grouping Operators - GroupBy - Comparer
[2] Linq„œŒwi 'Linq44'
[3] APL„" from " " salt " " earn " "last" "near" "FORM"
[4] APL„(+/¨^\¨ ' '=¨APL)‡¨APL
[5] APL„(-+/¨^\¨²¨' '=¨APL)‡¨APL
[6] a„((a¼a)=¼½a)/a„¹LCase ¨APL
[7] b„(›¨“¨(›a)¼¨LCase ¨APL)Þ¨LCase ¨APL
[8] c„b¼b
[9] APL„œ(›[Œio+1]((c=¼½c)/c)°.=c)Select ¨›APL
[10] Z„(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

1.7.6 GroupBy – Comparer, Mapped


This sample displays which elements from an input string array have the same letters. The sample used
GroupBy and an AnagramEqualityComparer class to group the elements into those with the same letters.
Note the conversion to uppercase.

The C# sample solution is deficient! In order to illustrate this I have added two more pairs of phrases to
the arguments used in the sample.

Linq45
LINQ APL Match?
FROM FORM FROM FORM 0

Page 38 of 69
SALT LAST SALT LAST
EARN NEAR EARN NEAR
AJAY AJAY
ASKOOLUM ASKOOLUM
DEBIT CARD DEBIT CARD BAD CREDIT
BAD CREDIT OWLS SLOW
SLOW
OWLS

The C# solution fails to allow for embedded spaces, although it does trim leading and trailing spaces,
which should be inconsequential in finding anagrams.

The C# solution also fails to cope with the case of the phrases, although it converts the result—not the
arguments—to uppercase.

The APL+Win solution, which corrects the C# deficiencies, is:

’ Z„Linq45;Linq;APL
[1] © Grouping Operators - GroupBy - Comparer, Mapped
[2] Linq„œŒwi 'Linq45'
[3] APL„UCase¨ " from " " salt " " earn " "last" "near" "FORM" "ajay"
"askoolum" "Debit Card" "Bad Credit" "OWLS" "slow"
[4] APL„(+/¨^\¨ ' '=¨APL)‡¨APL
[5] APL„(-+/¨^\¨²¨' '=¨APL)‡¨APL
[6] a„((a¼a)=¼½a)/a„¹APL
[7] b„ (›¨“¨(›a)¼¨APL)Þ¨APL
[8] c„b¼b
[9] APL„œ(›[Œio+1]((c=¼½c)/c)°.=c)Select ¨›APL
[10] Z„(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

1.8 Set Operators
As the name implies, this group of operators apply to vectors of numbers or literals. APL+Win is naturally
disposed to excel in this context.
1.8.1 Distinct - 1
The sample creates a sequence of unique factors by using the Distinct method on the integer vector of factors
to remove duplicates.

Linq46
LINQ 2 3 5
APL 2 3 5
Match? 1

’ Z„Linq46;Linq;APL
[1] © Set Operators: Distinct - 1
[2] Linq„Œwi 'Linq46' (R„2 2 3 5 5)
[3] APL„((R¼R)=¼½R)/R
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
[5] …0
[6] Linq46 2 2 3 5 5 © Original example
[7] Linq46 9 8 7 6 2 1 2 3 9 8 7 6 5 2 3 4 5 9 0 8 71

I have coded t he C# method to accept any arbitrary but compatible argument .

Linq46 9 8 7 6 2 1 2 3 9 8 7 6 5 2 3 4 5 9 0 8 71
LINQ 2 3 5 9 8 7 6 2 1 2 3 9 8 7 6 5 2 3 4 5 9 0 8 71
APL 2 3 5
Match? 1
1.8.2 Distinct - 2
This query returns the unique Category names by first selecting all of the category names from the product
list, then using Distinct to remove duplicate names.

Page 39 of 69
Linq47
LINQ APL Match?

Beverages Beverages 1
Condiments Condiments
Produce Produce
Meat/Poultry Meat/Poultry
Seafood Seafood
Dairy Products Dairy Products
Confections Confections
Grains/Cereals Grains/Cereals

The APL+Win function:

’ Z„Linq47;Linq;APL
[1] © Set Operators: Distinct - 2
[2] Linq„œŒwi 'Linq47'
[3] APL„(Œio+2)œ¨Œwi 'GetProductListEx' © ProductID ProductName Category
UnitPrice UnitsInStock
[4] APL„œ((APL¼APL)=¼½APL)/APL
[5] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.8.3 Union - 1
This query returns the unique elements of two integer arrays. The sample uses the Union method to create a
sequence that is a union of the two integer arrays with duplicate elements removed.

’ Z„Linq48;L;R;Linq;APL
[1] © Set Operators: Union - 1
[2] L„¹(2?10)?¨15 © With possible replication
[3] R„¹(2?10)?¨23 © With possible replication
[4] Linq„Œwi 'Linq48' L R
[5] L„((L¼L)=¼½L)/L
[6] R„((R¼R)=¼½R)/R
[7] APL„L,R~L
[8] Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)

This function is coded to use random arguments; therefore, it will return a different result simply because
the argument is different.
1.8.4 Union - 2
This query returns unique letters from Product and Customer names. The sample creates a sequence of
product first characters, a sequence of customer first characters, and then joins the two sets by using Union to
merge them and remove duplicates.

Linq49
LINQ APL Match?
’ Z„Linq49;L;R;Linq;APL
C C 1 [1] © Set Operators: Union - 2
A A [2] Linq„œŒwi 'Linq49'
G G [3] © (ProductID ProductName Category UnitPrice

Page 40 of 69
U U UnitsInStock) (© CustomerID CompanyName
N N Address City Region PostalCode Country Phone
M M Fax Orders[])
I I [4] L„1†¨(Œio+1)œ¨Œwi 'GetProductListEx'
Q Q [5] R„1†¨(Œio+1)œ¨Œwi 'GetCustomerListEx'
K K [6] L„((L¼L)=¼½L)/L
T T [7] R„((R¼R)=¼½R)/R
P P [8] APL„œL,R~L
S S [9] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL
R R (Linq−APL)
B B ’
J J
Z Z
V V
F F
E E
W W
L L
O O
D D
H H
1.8.5 Intersect - 1
This query returns a list of numbers that are common to two integer vectors. The sample uses Intersect to
create one sequence that contains the common values shared by vectors.

Linq50
L 3 5 13 0 10 6 4 8
R 12 2 2 22 9 4 13 21 8
LINQ 13 4 8
APL 13 4 8
Match? 1

The APL+Win function :

’ Z„Linq50;L;R;Linq;APL
[1] © Set Operators: Intersect - 1
[2] L„¹(2?10)?¨15 © With possible replication
[3] R„¹(2?10)?¨23 © With possible replication
[4] Linq„Œwi 'Linq50' L R
[5] APL„(L¹R)/L
[6] APL„((APL¼APL)=¼½APL)/APL
[7] Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)
[8] …0
[9] (L R)„(0 2 4 5 6 8 9) (1 3 5 7 8)

This function generates its arguments randonmly and uses each such argument and passes the same
argument to the C# method. The original arguments uses in the sample are shown in line [9].
1.8.6 Intersect - 2
This query returns the letters that are both the first letter of a Product name and the first letter of a Customer
name. The sample uses query statements to create two sequence - the first letters of Product names and the
first letter of Customer names, then uses Intersect to create a sequence of letters common to both.

Linq51
LINQ APL Match? ’ Z„Linq51;L;R;Linq;APL
[1] © Set Operators: Intersect - 2
C C 1 [2] Linq„œŒwi 'Linq51'
A A [3] © (ProductID ProductName Category UnitPrice
G G UnitsInStock) (© CustomerID CompanyName
N N Address City Region PostalCode Country Phone

Page 41 of 69
M M Fax Orders[])
I I [4] L„1†¨(Œio+1)œ¨Œwi 'GetProductListEx'
Q Q [5] R„1†¨(Œio+1)œ¨Œwi 'GetCustomerListEx'
K K [6] APL„(L¹R)/L
T T [7] APL„œ((APL¼APL)=¼½APL)/APL
P P [8] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL
S S (Linq−APL)
R R

B B
V V
F F
E E
W W
L L
O O
1.8.7 Except - 1
This query returns numbers that are in one integer array, but not another. The sample uses Except to create a
sequence that contains the values from numbersA that are not also in numbersB.

Linq52
L 9 11 7 0 3 12 8
R 12 2 18 22 0 19 5
LINQ 9 11 7 3 8
APL 9 11 7 3 8
Match? 1

’ Z„Linq52;L;R;Linq;APL
[1] © Set Operators: Except 1
[2] L„¹(2?10)?¨15 © With possible replication
[3] R„¹(2?10)?¨23 © With possible replication
[4] Linq„Œwi 'Linq52' L R
[5] L„((L¼L)=¼½L)/L
[6] APL„(~L¹R)/L
[7] Z„•œ(›¨'L' 'R' 'LINQ' 'APL' 'Match?'),¨L R Linq APL (Linq−APL)
[8] …0
[9] (L R)„(0 2 4 5 6 8 9)(1 3 5 7 8)

1.8.8 Except - 2
This query returns the first character of product names that are not also the first character of customer names.
After getting the first characters of product and customer names using query expressions, the sample uses
Except to create a sequence of product characters that doesn't include first characters of customer names.

Linq53
LINQ APL Match?
U U 1
J J
Z Z

’ Z„Linq53;L;R;Linq;APL
[1] © Set Operators: Except - 2
[2] Linq„œŒwi 'Linq53'
[3] © (ProductID ProductName Category UnitPrice UnitsInStock) (© CustomerID
CompanyName Address City Region PostalCode Country Phone Fax Orders[])
[4] L„1†¨(Œio+1)œ¨Œwi 'GetProductListEx'
[5] R„1†¨(Œio+1)œ¨Œwi 'GetCustomerListEx'
[6] L„((L¼L)=¼½L)/L
[7] APL„œ(~L¹R)/L
[8] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Page 42 of 69
1.9 Conversion Operators
From an APL+Win point of view, this group of operators contain trivial examples. However, in a C# context,
the examples illustrate the ease with which several operations can be carries out seamlessly using LINQ
techniques. The other purpose of these examples is too illustrate the conversion from one complex type to
another: APL+Win does not support complex types but can reproduce the results quite easily.
1.9.1 To Array
This sample generates a vector of double values by first using a query expression with orderby to create a
sorted sequence of doubles, then ToArray to generate an array from the sequence.

Linq54
LINQ 4.1 2.3 1.7
APL 4.1 2.3 1.7
Match? 1.0

The APL+Win function:

’ Z„Linq54;Linq;APL
[1] © Conversion Operators: To Array
[2] APL„1.7 2.3 1.9 4.1 2.9
[3] Linq„Œwi 'Linq54' APL
[4] APL„(2|+\(½APL)/1)/APL[”APL] © Œio independent
[5] Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Note that the value for ‘Match’ is shown as a double: this is a consequence of line [5], that is of formatting
the result. It is Boolean as you would expect.
1.9.2 To List
This sample generates an array of string values by first using a query expression with orderby to create a
sorted sequence of strings from a string array, then toList to generate a List from the sequence. The
conversion operation is from an array to a list.

Linq55
LINQ apple blueberry cherry
APL apple blueberry cherry
Match? 1

The APL+Win function:

’ Z„Linq55;Linq;APL
[1] © Conversion Operators: To List
[2] Linq„Œwi 'Linq55'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„APL[“Œav¼œAPL]
[5] Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.9.3 To Dictionary
This sample uses anonymous types to create a data structure of people and their scores. It then uses
ToDictionary to generate a dictionary from the sequence and its key expression.

A Dictionary is a complex data type in C#. Essentially, it is a name/value pair array with a notable
difference: the array is indexed by the name and not an ordinal value that would represent an index. For
example:

Console.WriteLine("Bob's score: {0}", scoreRecordsDict["Bob"]);

This yields the following result:

Bob's score: {Name=Bob, Score=40}

Page 43 of 69
This is not available in APL+Win; however, it is quite straightforward to simulate the same effect.

Linq56
LINQ Bob 40
APL Bob 40
Match? 1

’ Z„Linq56;Linq;APL
[1] © Conversion Operators: To Dictionary
[2] Linq„Œwi 'Linq56'
[3] APL„("Alice" 50) ("Bob" 40) ("Cathy" 45)
[4] APL„,œ((›"Bob") −¨†¨APL)/APL
[5] Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.9.4 OfType
This sample uses OfType to return only the elements of the array that are of type double.

This sample is rather tricky for APL+Win: C# recognises a double number when a decimal point is
present, even when the decimal part is zero. In many situations, APL+Win silently converts any number with a
decimal part of zero to an integer. The objective is to identify elements of different types: for convenience, I
have altered the decimal part of doubles in the argument of this operator to a non-zero value.

Linq57
LINQ 1.1 7.1
APL 1.1 7.1
Match? 1.0

’ Z„Linq57;Linq;APL
[1] © Conversion Operators: OfType
[2] Linq„Œwi 'Linq57'
[3] APL„(0/0) 1.1 "two" 3 "four" 5 "six" 7.1
[4] APL„(645=Œdr ¨APL)/APL
[5] Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.10 Element Operators
This group of operators illustrate techniques for working with elements of vectors. This group contains trivial
examples, which are valuable nonetheless in one vital respect: when studying the examples, consider the
comparative readability of the C# and APL+Win code.
1.10.1 First - Simple
This sample uses ‘First’ to return the first matching element as a Product, instead of as a sequence containing
a Product; a Product is a complex data type containing named data items, in this case, ProductID,
ProductName, Category, UnitPrice, and UnitsInStock

Linq58
LINQ 12 Queso Manchego La Pastora Dairy Products 38 86
APL 12 Queso Manchego La Pastora Dairy Products 38 86
Match? 1

This function produces the matching result:

Page 44 of 69
’ Z„Linq58;Linq;APL
[1] © Element Operators: First - Simple
[2] Linq„Œwi 'Linq58'
[3] APL „Œwi 'GetProductListEx'
[4] APL„,œ(<\12=Œioœ¨APL)/APL
[5] Z„œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Note that this query returns only the first matching element.
1.10.2 First – Condition (missing at URL)
This sample uses ‘First’ to find the first element in a vector that starts with 'o'.

Linq59
LINQ one APL one Match? 1

The function:

’ Z„Linq59;Linq;APL
[1] © Element Operators: First - Condition
[2] Linq„Œwi 'Linq59'
[3] APL„ "zero" "one" "two" "three" "four" "five" "six" "seven" "eight"
"nine"
[4] APL„†(¹'o'=†¨APL)/APL
[5] Z„(›¨'LINQ' 'APL' 'Match?'),¨,¨Linq APL (Linq−APL)

1.10.3 First - Indexed
This example returns the first element of an integer vector that is both even and exists at an even index within
the vector. The code sample, as given at the URL, does not work! The culprit line is:

int evenNum = numbers.First((num, index) => (num % 2 == 0) && (index % 2 == 0));

It generates the following error:

Delegate 'System.Func<int,bool>' does not take '2' arguments

I can only surmise that Microsoft published the code sample prior to the release of C# 3.0. The corrected
line that produces the expected result is:

int evenNum = numbers.Where((num, index) => (num % 2 == 0) && (index % 2 == 0)).First();

The APL function is:

’ Z„Linq60;Linq;APL
[1] © Element Operators: First - Indexed
[2] Linq„œŒwi 'Linq60'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„†((~2|APL)^~2|(-Œio)+APL¼APL)/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The matching result is:

Linq60
LINQ 6
APL 6
Match? 1
1.10.4 FirstOrDefault - Simple
This query returns the default value of a data type; the example uses the integer data type whose default
value is 0. This is possible only in a declarative language such as C#; APL is not a declarative language.

’ Z„Linq61;Linq;APL

Page 45 of 69
[1] © Element Operators: FirstOrDefault - Simple
[2] Linq„œŒwi 'Linq61'
[3] © Create a variable from which the data type integer (323) can be
inferred
[4] APL„9 10
[5] © Create an empty value from the variable - this emulates declaration
[6] APL„0/APL
[7] © Take the first or default value
[8] APL„†APL
[9] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

However, it is possible to demonstrate the concept using APL+Win, using inference.

1.10.5 FirstOrDefault - Condition


This query uses ‘FirstOrDefault’ to return the first product whose ProductID is 789; if there is no match,
null is returned. Given the lack of support for the null data type in APL+Win, it is straightforward to modify
the method to return true or false.

Linq62
LINQ 0
APL 0
Match? 1

The function:

’ Z„Linq62;Linq;APL
[1] © Element Operators: FirstOrDefault - Condition
[2] Linq„œŒwi 'Linq62'
[3] APL„789¹Œioœ¨Œwi 'GetProductListEx' © First element is ProductID
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.10.6 FirstOrDefault - Indexed
This query uses ‘FirstOrDefault’ to find the first number in a vector that is within 0.5 of its position in the vector
and returns either the first match or the default value of data type double, which is null.

This one is tricky! Not least because the sample code at the URL does not work. Even with correction, it
still presents two problems for APL+Win:

• It does not recognise a method in a COM DLL that returns null.

• A vector cannot contain a null value that is recognisable as null by the COM Server.

I propose a workaround: I will modify the query to return either the first number from its argument that is
between -0.5 and 0.5 of its position—remember the position is in index origin zero—or return the smallest
number if none is found.

Page 46 of 69
Let us explore the deliverable without null values, without recourse to the COM Server, and with the
following APL+Win function:

’ Z„Linq63Ex APL
[1] © Element Operators: FirstOrDefault - Indexed
[2] © Linq„œŒwi 'Linq63'
[3] © APL„1.7 2.3 4.1 1.9 2.9
[4] APL„(0=+/×(³¯0.5 0.5°.+(-Œio)+¼½APL)-[Œio]APL)/APL
[5] :if 0=½APL
[6] Z„˜/Ð
[7] :else
[8] Z„нAPL
[9] :endif
[10] ©Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The APL+Win that delivers the same solution as the COM method—subject to the limitations discussed—
is as follows:

’ Z„Linq63;Linq;APL
[1] © Element Operators: FirstOrDefault - Indexed
[2] Linq„œŒwi 'Linq63'
[3] APL„1.7 2.3 4.1 1.9 2.9
[4] APL„(0=+/×(³¯0.5 0.5°.+(-Œio)+¼½APL)-[Œio]APL)/APL
[5] :if 0=½APL
[6] APL„˜/0
[7] :else
[8] APL„нAPL
[9] :endif
[10] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The result:

Linq63
Actual Result 1
LINQ 1.797693135E308
APL 1.797693135E308
Match? 1.000000000E0

There are no elements within ±0.5 of its position in the vector.


1.10.7 ElementAt
This query returns the fourth number less that 5 in an integer vector. The sample first uses a query expression
then uses ‘ElementAt’ to retrieve the fourth number from this sequence. Since ElementAt uses 0-based
indexing, 3 is passed to the method to retrieve the fourth element.

Linq64
LINQ 2
APL 2
Match? 1

Page 47 of 69
The matching result is produced by the following APL+Win function:

’ Z„Linq64;Linq;APL
[1] © Element Operators: ElementAt
[2] Linq„œŒwi 'Linq64'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„н3‡(APL<5)/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.11 Generation Operators
This group of queries or operators generate sequences: the samples provided generate sequences of
integers. What is striking is the simplicity of the APL+Win code to generate identical results produced by the
C# code.
1.11.1 Range
This query uses ‘Range’ to generate a sequence of numbers from 100 to 149 and then classifies each
element of the resulting vector as either odd or even.

’ Z„Linq65;Linq;APL
[1] © Generation Operators: Range
[2] Linq„³œŒwi 'Linq65'
[3] APL„100+(-Œio)+¼50
[4] APL„³œAPL (('even' 'odd')[Œio+2|APL])
[5] Z„•(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

For APL+Win, this group of operators are trivial or routine.


1.11.2 Repeat
In the context of APL+Win, this is another trivial example. Moreover, the APL+Win technique of replication
applies equally to other types of data.

Linq66
LINQ 7 7 7 7 7 7 7 7 7 7
APL 7 7 7 7 7 7 7 7 7 7
Match? 1

The APL+Win function:

’ Linq66;Linq;APL
[1] © Generation Operators: Repeat
[2] Linq„Œwi 'Linq66'
[3] APL„10/7
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Page 48 of 69
1.12 Quantifiers
This group of operators apply criteria within the elements of vectors. APL+Win does this type of processing
quite naturally using standard primitives.
1.12.1 Any - Simple
This query determines whether any words in a string array contain the character sequence 'ei'; it returns
1 or true if at least one element contains the designated substring.

Linq67
LINQ 1
APL 1
Match? 1

’ Z„Linq67;Linq;APL
[1] © Quantifiers: Any - Simple
[2] Linq„œŒwi 'Linq67'
[3] APL„"believe" "relief" "receipt" "field"
[4] APL„1¹(›'ie')Ÿ.º¨APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.2 Any - Indexed
The code sample given at the URL is plainly wrong! This sample determines whether any numbers in an
integer array are the negative of their position in the vector.

The culprit line is:

bool negativeMatch = numbers.Any((n, index) => n == -index);

The correction is:

bool negativeMatch = numbers.Select((n, index) => new { n, index }).Any(x => x.n == -x.index);

This is an appropriate point to pause and consider the readability of APL+Win code, comparing it to the
corresponding C# code.

Linq68
LINQ 0
APL 0
Match? 1

This function produces the matching result:

’ Z„Linq68;Linq;APL
[1] © Quantifiers: Any - Indexed
[2] APL„¯9 4 ¯8 3 ¯5 2 ¯1 6 ¯7
[3] Linq„Œwi 'Linq68' APL
[4] APL„×½(APL=-(-Œio)+¼½APL)/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.3 Any - Grouped

1.12.4 All - Simple


This query uses All to determine whether an array contains only odd numbers. This type of query
represents very basic APL+Win skills of novices!

Linq70
LINQ 1
APL 1

Page 49 of 69
Match? 1

This function produces the matching result:

’ Z„Linq70;Linq;APL
[1] © Quantifiers: All - Simple
[2] Linq„œŒwi 'Linq70'
[3] APL„1 11 3 19 41 65 19
[4] APL„1^.=2|APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.5 All - Indexed
The sample code given for this example is also incorrect. The following line is incorrect:

bool allLower = lowNumbers.All((num, index) => num < highNumbers[index]);

It creates an error that prevents the code from compiling:

System.Func<int,bool>' does not take '2' arguments

The correct line is:

bool allLower = lowNumbers.Select((n, index) => new { n, index }).All(x => x.n < highNumbers[x.index]);

This sample determines whether every element of an integer vector is lower than the corresponding
element in another integer vector; the two vectors are conformable.

Linq71
LINQ 1
APL 1
Match? 1

’ Z„Linq71;Linq;APL;lowNumbers;highNumbers
[1] © Quantifiers: All - Indexed
[2] lowNumbers„ 1 11 3 19 41 65 19
[3] highNumbers„ 7 19 42 22 45 79 24
[4] Linq„Œwi 'Linq71' lowNumbers highNumbers
[5] APL„lowNumbers^.<highNumbers
[6] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.12.6 All - Grouped
This query returns all of the products in categories where none of the products in that category is out of stock.
The sample first uses group by to group the products into categories. It then uses Any to find those groups
that have no products out of stock. Finally, the select clause creates elements of the return sequence
consisting of the Category as the key and the associated group of products.

1.13 Aggregate Operators


There is a way of working with C# with the reassurance of the APL+Win comfort zone: this group of operators
illustrate this admirably.
1.13.1 Count - Simple
This query returns the count of distinct elements of an integer vector.

Linq73
LINQ 3
APL 3
Match? 1

Page 50 of 69
The APL+Win function that produces the matching result:

’ Z„Linq73;Linq;APL
[1] © Aggregate Operators - Count - Simple
[2] Linq„Œwi 'Linq73'
[3] APL„2 2 3 5 5
[4] APL„н½((APL¼APL)=¼½APL)/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Note that the APL+Win solution needs to match the C# result: scalar for scalar, see line [4].
1.13.2 Count - Conditional
This sample finds the number of odd elements of an integer array. This describes the C# inner workings:

“The sample passes a lambda expression to Count that it uses to perform the test, while Count handles the
iteration and maintains the running total.”

I am finding the documentation of the solution difficult to understand, let alone the solution itself! In
contrast, the APL+Win solution could not be simpler.

Linq74
LINQ 5
APL 5
Match? 1

’ Z„Linq74;Linq;APL
[1] © Aggregate Operators - Count - Conditional
[2] Linq„œŒwi 'Linq74'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„2+.|APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.3 Count - Indexed
The code sample provided at the URL does not compile. The culprit line is:

int oddEvenMatches = numbers.Count((n, index) => n % 2 == index % 2);

It causes the following error:

Delegate 'System.Func<int,bool>' does not take '2' arguments

The correct line is:

int oddEvenMatches = numbers.Where((n, index) => n % 2 == index % 2).Count();

Linq75
LINQ 4
APL 4
Match? 1

’ Z„Linq75;Linq;APL
[1] © Aggregate Operators - Count - Indexed
[2] Linq„œŒwi 'Linq75'
[3] numbers„5 4 1 3 9 8 6 7 2 0
[4] APL„(2|numbers)+.^2|(-Œio)+¼½numbers © odd numbers at odd
positions (index origin zero)
[5] APL„APL+(~2|numbers)+.^~2|(-Œio)+¼½numbers © even numbers at even
positions (index origin zero)
[6] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Page 51 of 69
1.13.4 Count - Nested
This sample uses Count to return a list of customers and how many orders each has; it includes customers
with nil orders.

’ Z„Linq76;Linq;APL
[1] © Aggregate Operators - Count - Nested
[2] Linq„œŒwi 'Linq76'
[3] APL„Œwi 'GetCustomerListEx'
[4] CustomerID„Œioœ¨APL
[5] OrderCount„¹½¨(Œio+9)œ¨APL
[6] APL„³œCustomerID OrderCount
[7] Z„•(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.5 Count - Grouped


This sample prints each category and the number of products in that category. The sample first uses group by
to group the products according to their categories. Then it uses select to create an anonymous for each
category that contains the category and number of products.

Linq77
LINQ APL Match?
Beverages 12 Beverages 12 1
Condiments 12 Condiments 12
Produce 5 Produce 5
Meat/Poultry 6 Meat/Poultry 6
Seafood 12 Seafood 12
Dairy Products 10 Dairy Products 10
Confections 13 Confections 13
Grains/Cereals 7 Grains/Cereals 7

’ Z„Linq77;Linq;APL;a
[1] © Aggregate Operators - Count - Grouped
[2] Linq„œŒwi 'Linq77'
[3] APL„Œwi 'GetProductListEx'
[4] Category„(Œio+2)œ¨APL
[5] a„((Category¼Category)=¼½Category)/Category
[6] APL„³œa (+š(a¼Category)°.=¼½a)
[7] Z„•(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

1.13.6 Sum - Simple
This query uses Sum to find the total of all of the numbers in an integer array. Sounds familiar?

LINQ 45
APL 45
Match? 1

Page 52 of 69
’ Z„Linq78;Linq;APL
[1] © Aggregate Operators - Sum - Simple
[2] Linq„Œwi 'Linq78'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„+/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.7 Sum - Projection
This sample uses Sum to find the total number of characters in all of the words in a string array.

Linq79
LINQ 20
APL 20
Match? 1

’ Z„Linq79;Linq;APL
[1] © Aggregate Operators - Sum - Projection
[2] Linq„,Œwi 'Linq79'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„,¹+/½¨APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.8 Sum - Grouped
This query returns, for each category, the total number of units in stock for all products in that category.

Linq80
LINQ APL Match?
Beverages 559 Beverages 559 1
Condiments 507 Condiments 507
Produce 100 Produce 100
Meat/Poultry 165 Meat/Poultry 165
Seafood 701 Seafood 701
Dairy Products 393 Dairy Products 393
Confections 386 Confections 386
Grains/Cereals 308 Grains/Cereals 308

’ Z„Linq80;Linq;APL
[1] © Aggregate Operators - Sum - Grouped
[2] Linq„œŒwi 'Linq80'
[3] APL„Œwi 'GetProductListEx'
[4] (Category UnitsInStock)„(Œio+2 4)œ¨¨›APL
[5] DistinctCategory„((Category¼Category)=¼½Category)/Category
[6]
UnitsInStock„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]Un
itsInStock © Separate UnitsInStock by Category (into columns)
[7] APL„³œDistinctCategory (+šUnitsInStock)
[8] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.13.9 Min - Simple
This sample uses Min to get the lowest number in an integer array.

Linq81
LINQ 0
APL 0
Match? 1

’ Z„Linq81;Linq;APL
[1] © Aggregate Operators - Min - Simple
[2] Linq„œŒwi 'Linq81'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„˜/APL

Page 53 of 69
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.10 Min - Projection
This sample uses Min to get the length of the shortest word in a string array.

Linq82
LINQ 5
APL 5
Match? 1

’ Z„Linq82;Linq;APL
[1] © Aggregate Operators - Min - Projection
[2] Linq„œŒwi 'Linq82'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„н¹˜/½¨APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.11 Min - Grouped
This query finds, for each category, the lowest of all product prices.

Linq83
LINQ APL Match?
Beverages 4.50 Beverages 4.50 1
Condiments 10.00 Condiments 10.00
Produce 10.00 Produce 10.00
Meat/Poultry 7.45 Meat/Poultry 7.45
Seafood 6.00 Seafood 6.00
Dairy Products 2.50 Dairy Products 2.50
Confections 9.20 Confections 9.20
Grains/Cereals 7.00 Grains/Cereals 7.00

’ Z„Linq83;Linq;APL;UnitPrice;Category;DistinctCategory
[1] © Aggregate Operators - Min - Grouped
[2] Linq„œŒwi 'Linq83'
[3] APL„Œwi 'GetProductListEx'
[4] (Category UnitPrice)„(Œio+2 3)œ¨¨›APL
[5] DistinctCategory„((Category¼Category)=¼½Category)/Category
[6]
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
[7] ((0=,UnitPrice)/,UnitPrice)„˜/Ð
[8] APL„³œDistinctCategory (˜šUnitPrice)
[9] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

Pause a moment and consider this question: what is the purpose of line [7]?
1.13.12 Min - Elements
This query returns the cheapest price in each category

Page 54 of 69
’ Z„Linq84;Linq;APL;Category;UnitPrice;DistinctCategory
[1] © Aggregate Operators - Max - Grouped
[2] Linq„œŒwi 'Linq84'
[3] APL„Œwi 'GetProductListEx'
[4] (Category UnitPrice)„(Œio+2 3)œ¨¨›APL
[5] DistinctCategory„((Category¼Category)=¼½Category)/Category
[6]
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
[7] ((0=,UnitPrice)/,UnitPrice)„˜/Ð
[8] APL„›[Œio+1](¹Ÿ/(›[Œio]UnitPrice)¹¨˜šUnitPrice)šœAPL
[9] APL„œAPL[“DistinctCategory¼(Œio+2)œ¨APL] © Reorder in original category
order
[10] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

The APL+Win solution has an interesting twist: refer to line [7].
1.13.13 Max - Simple
This sample uses Max to get the highest number in an integer array.

Linq85
LINQ 9
APL 9
Match? 1

’ Z„Linq85;Linq;APL
[1] © Aggregate Operators - Max - Simple
[2] Linq„œŒwi 'Linq85'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„—/APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.14 Max - Projection
This sample uses Max to get the length of the longest word in a string array.

Linq86
LINQ 9
APL 9
Match? 1

’ Z„Linq86;Linq;APL
[1] © Aggregate Operators - Max - Projection
[2] Linq„Œwi 'Linq86'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„н¹—/½¨APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.15 Max - Grouped
This query returns the most expensive price in each category.

Linq87
LINQ APL Match?
Beverages 263.50 Beverages 263.50 1
Condiments 43.90 Condiments 43.90
Produce 53.00 Produce 53.00
Meat/Poultry 123.79 Meat/Poultry 123.79
Seafood 62.50 Seafood 62.50
Dairy Products 55.00 Dairy Products 55.00
Confections 81.00 Confections 81.00
Grains/Cereals 38.00 Grains/Cereals 38.00

Page 55 of 69
’ Z„Linq87;Linq;APL;Category;UnitPrice;DistinctCategory
[1] © Aggregate Operators - Max - Grouped
[2] Linq„œŒwi 'Linq87'
[3] APL„Œwi 'GetProductListEx'
[4] (Category UnitPrice)„(Œio+2 3)œ¨¨›APL
[5] DistinctCategory„((Category¼Category)=¼½Category)/Category
[6]
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
[7] APL„³œDistinctCategory (—šUnitPrice)
[8] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.13.16 Max - Elements
This sample finds the most expensive products in each category.

’ Z„Linq88;Linq;APL;Category;UnitPrice;DistinctCategory
[1] © Aggregate Operators - Max - Elements
[2] Linq„œŒwi 'Linq88'
[3] APL„Œwi 'GetProductListEx'
[4] (Category UnitPrice)„(Œio+2 3)œ¨¨›APL
[5] DistinctCategory„((Category¼Category)=¼½Category)/Category
[6]
UnitPrice„((DistinctCategory¼Category)°.=¼½DistinctCategory)×[Œio]UnitP
rice © Separate prices by Category (into columns)
[7] APL„›[Œio+1](¹Ÿ/(›[Œio]UnitPrice)¹¨—šUnitPrice)šœAPL
[8] APL„œAPL[“DistinctCategory¼(Œio+2)œ¨APL] © Reorder in original category
order
[9] Z„('LINQ' 'APL' 'Match?'),[Œio-0.5] Linq APL (Linq−APL)

1.13.17 Average - Simple
This sample uses Average to get the average of all values of an integer array.

Linq89
LINQ 4.5
APL 4.5
Match? 1.0

’ Z„Linq89;Linq;APL
[1] © Aggregate Operators - Average - Simple
[2] Linq„œŒwi 'Linq89'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„н(+/APL)÷½APL © Result is scalar: would not match if a single
element
vector
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.18 Average - Projection
This sample uses Average to get the average length of the words in the string array.

Page 56 of 69
Linq90
LINQ 6.666666667
APL 6.666666667
Match? 1.000000000

’ Z„Linq90;Linq;APL
[1] © Aggregate Operators - Average - Projection
[2] Linq„Œwi 'Linq90'
[3] APL„ "cherry" "apple" "blueberry"
[4] APL„н(¹+/½¨APL)÷½APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.19 Average - Grouped

1.13.20 Fold - Simple


This sample finds the product of all elements of an array of doubles. The samples uses Fold to create a
running product on the array, passing each element in turn to the lambda expression that performs the
multiplication.

Linq92
LINQ 88.33081
APL 88.33081
Match? 1.00000

’ Z„Linq92;Linq;APL
[1] © Aggregate Operators: Fold - Simple
[2] Linq„Œwi 'Linq92'
[3] APL„×/1.7 2.3 1.9 4.1 2.9
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.13.21 Fold - Seed
This sample subtracts a sequence of integers from a starting value, simulating withdrawals from an account.
While there is still cash left in the account, the withdrawal succeeds. The sample uses Fold to pass each
withdrawal value in turn to the lambda expression that performs the subtraction.

Linq93
startBalance 37
attemptedWithdrawals 29 140 60 4
LINQ 17
APL 17
Match?

’ Z„Linq93;Linq;APL;startBalance;attemptedWithdrawals;i;R
[1] © Aggregate Operators - Fold - Seed
[2] startBalance„?1000
[3] attemptedWithdrawals„¹(2?5)?¨150 © With possible replication
[4] Linq„œŒwi 'Linq93' startBalance attemptedWithdrawals
[5] i„startBalance‰+\attemptedWithdrawals
[6] APL„startBalance-+/i/attemptedWithdrawals
[7] attemptedWithdrawals„(~i)/attemptedWithdrawals
[8] :for R :in attemptedWithdrawals
[9] :if APL>R
[10] APL„APL-R
[11] :endif
[12] :endfor
[13] Z„•œ(›¨'startBalance' 'attemptedWithdrawals' 'LINQ' 'APL'
'Match?'),¨startBalance attemptedWithdrawals Linq APL (Linq−APL)

The APL+Win function generates the arguments it uses and passes to C# randomly. This function is
coded such that it allows for easy step-wise debugging.

Page 57 of 69
1.14 Miscellaneous Operators

1.14.1 Concat - 1
This query merges two integer vectors into a single sequence. The sample uses ‘Concat’ to create the
sequence with each array's values, one after the other.

This is quite simply ‘Catenate’ in APL+Win without the sophistication of optional axes specifications.

Linq94
LINQ 0 2 4 5 6 8 9 1 3 5 7 8 APL 0 2 4 5 6 8 9 1 3 5 7 8 Match? 1

The APL+Win function:

’ Z„Linq94;Linq;APL
[1] © Miscellaneous Operators - Concat - 1
[2] Linq„Œwi 'Linq94'
[3] APL„0 2 4 5 6 8 9,1 3 5 7 8
[4] Z„•(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.14.2 Concat - 2
This query returns all customer names followed by all product names. The LINQ solution is elaborate because
the language operates at scalar level and needs to iterate to cope with arrays.

The APL+Win function:

’ Z„Linq95;Linq;APL
[1] © Miscellaneous Operators - Concat - 2
[2] Linq„œŒwi 'Linq95'
[3] © (ProductID ProductName Category UnitPrice UnitsInStock) (© CustomerID
CompanyName Address City Region PostalCode Country Phone Fax Orders[])
[4] APL„(œ(Œio+1)œ¨Œwi 'GetCustomerListEx') (œ(Œio+1)œ¨Œwi
'GetProductListEx')
[5] APL„œ,[Œio]/((›¹0,—/¯1†¨½¨APL)—½¨APL)†¨APL
[6] Z„•(›¨'LINQ' 'APL' 'Match?'),[Œio-0.5]Linq APL (Linq−APL)

This operation is simply catenation on the x axis.
1.14.3 EqualAll - 1
This query determines if two string vectors have the same elements in the same order. It uses ‘EqualAll’ to
compare the two vectors, element by element. For string vectors in C#, the APL+Win equivalent is a nested
vector. The corresponding structure for nested vector in C# is a jagged vector. However, the argument to this
query is not a jagged (or nested) vector but simply a single dimensional array.

Linq96
LINQ 1
APL 1

Page 58 of 69
Match? 1

The APL+Win function:

’ Z„Linq96;Linq;APL
[1] © Miscellaneous Operators - EqualAll - 1
[2] Linq„œŒwi 'Linq96'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„^/¹APL−¨APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

1.14.4 EqualAll - 2
This repeats of the previous query using arguments that will create the opposite result, that is, false. APL+Win
returns a matching result.

Linq97
LINQ 0
APL 0
Match? 1

The function is:

’ Z„Linq97;Linq;APL
[1] © Miscellaneous Operators - EqualAll - 2
[2] Linq„œŒwi 'Linq97'
[3] APL„"cherry" "apple" "blueberry"
[4] APL„^/¹APL−¨"apple" "blueberry" "cherry"
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The function Linq96Ex using the arguments used in this function will return a result that is true.
1.14.4.1 EqualAll - 2 : Extension
This query is looking to match not just the elements but also the order in which they occur. A useful extension
is to be able to determine if two vectors match simply by content irrespective of the order of the individual
elements. The APL+Win solution is:

’ Z„Linq97Ex;APL;APL2
[1] © Miscellaneous Operators - Element All
[2] APL„"cherry" "apple" "blueberry"
[3] APL2„"apple" "cherry" "blueberry"
[4] Z„APL^.¹APL2

Linq97Ex
1
1.15 Custom Sequence Operators
Some C# LINQ operators, like this one, are as old as APL itself. The C# solution is18:

18
Copied from the Visual Studio 2008 project, hence the line numbers.

Page 59 of 69
This appears deceptively simple; however, ‘Combine’ is defined as follows19:

It is clear that C# not only uses a different jargon to describe standard, even routine, APL+Win
functionality but also offers comparative solutions that are comparatively much more complicated. Does this
shed a new light on the arguments relating to APL+Win readability?
1.15.1 Combine
This query returns the dot product of two integer vectors. It uses a user-created sequence operator, Combine,
to calculate the dot product, passing it a lambda function to multiply two vectors, element by element, and
finally returns the sum as the result.

Linq98
LINQ 109
APL 109
Match? 1

This functionality in APL+Win is basic, going back to first generation APL; the function is defined as
follows:

’ Z„Linq98;Linq;APL
[1] © Custom Sequence Operators - Combine
[2] Linq„Œwi 'Linq98'
[3] APL„0 2 4 5 6+.×1 3 5 7 8
[4] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)
[5] © Error in public static class CustomSequenceOperators

1.15.1.1 C#:APL+Win comparison
Consider another example, returning iota 10 in index origin 1 and 0.

The C# solution using LINQ:

public int[] Linq98Ex_IOTA(int io, int arg)


{
return Enumerable.Range(io, arg).ToArray();
}

19
This is defined incorrectly at the URL.

Page 60 of 69
The APL+Win solution:

’ Z„Linq98Ex_IOTA;Linq;APL;Match
[1] © Ajay Askoolum
[2] Linq„(Œwi 'Linq98Ex_IOTA' 1 10)(Œwi 'Linq98Ex_IOTA' 0 10)
[3] APL„(+\10/1) ((-Œio)+¼10)
[4] Match„Linq−APL
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Neither solution incorporates any argument validation or error trapping in order to demonstrate the bare
minimum code to deliver a simple result. The result:

Linq98Ex_IOTA
LINQ 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9
APL 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9
Match? 1

What does this say about APL+Win versus C# code maintenance and/or the rapid application
development credentials of the two languages?
1.16 Query Execution
This group of queries attempt to illustrate how a query is defined for deferred, immediate, and re-use
execution.
1.16.1 Deferred
A query is coded such that its execution is deferred until data is requested from it. I am not sure that I
understand the subtleties; however, it is possible to emulate the same behaviour.

’ Z„Linq99;Linq;APL
[1] © Query Execution - Deferred
[2] Linq„Œwi 'Linq99'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„–¨('+\(½APL)/1') ('(~Œio)+¼½APL')
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Reading from right-to-left, line [4] defines a query whose execution is deferred until it is executed.

Linq99
LINQ 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
APL 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
Match? 1
1.16.2 Immediate
The query executes immediately.

Linq100
LINQ 1 2 3 4 5 6 7 8 9 10 10 10 10 10 10 10 10 10 10 10
APL 1 2 3 4 5 6 7 8 9 10 10 10 10 10 10 10 10 10 10 10
Match? 1

The APL+Win function:

’ Z„Linq100;Linq;APL
[1] © Query Execution - Immediate
[2] Linq„Œwi 'Linq100'
[3] APL„5 4 1 3 9 8 6 7 2 0
[4] APL„(+\(½APL)/1) ((½APL)/½APL)
[5] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

Page 61 of 69
1.16.3 Query Reuse
The query is defined such that it can be re-used subsequently—within the same scope—with different
arguments.

’ Z„Linq101;Linq;APL;Reuse
[1] © Query Execution - Query Reuse
[2] Linq„Œwi 'Linq101'
[3] 0 0½Œdef '’ R„Reuse R',Œtcnl,'[1] R„(Rˆ3)/R’'
[4] APL„5 4 1 3 9 8 6 7 2 0
[5] APL„(Reuse APL) (Reuse -APL)
[6] Z„•œ(›¨'LINQ' 'APL' 'Match?'),¨Linq APL (Linq−APL)

The key concept is that the query is accessible for re-use within the same scope. The APL+Win solution
emulates this by defining the query as a local function; the results match.

Linq101
LINQ 1 3 2 0 ¯5 ¯4 ¯1 ¯3 ¯9 ¯8 ¯6 ¯7 ¯2 0
APL 1 3 2 0 ¯5 ¯4 ¯1 ¯3 ¯9 ¯8 ¯6 ¯7 ¯2 0
Match? 1

1.17 Time for 101 APL+Win samples?


The intrinsic value of the 101 samples is not that they provide re-usable code or that they teach the C #
language; the value lies in the fact that they provide worked examples. As such, the samples provide a basis
on which to promote familiarity with key concepts and to enhance developer confidence. Sadly, (and wrongly,
in my opinion) APL has shirked this strategy for gaining wider acceptance.

One hundred and one LINQ samples delivered side by side in C# (the contemporary flagship language)
and APL+Win: I hope this bears witness to the credentials of APL+Win as a contemporary (where the Dot Net
Framework dominates) development tool.
1.17.1 Some eligible topics

1.17.1.1 Example 1 - Return the sign of all elements of a numeric vector/array

-1 Negative
0 Zero
1 Positive
Null Null or Empty

1.17.1.2 Example 2 - Return the magnitude indicator of all elements of a numeric vector/array
, given a range

-2 Is greater than Maximum


-1 Is equal to Maximum
0 Within minimum and maximum
1 Is equal to Minimum
2 Is less than Minimum

1.17.1.3 Example 3 – Apply the percentage of increase

Salary 12,345 67,654 9,876 67,542 78,345 32,456


Band 0 8 0 9 3 1

Next consider the percentage increase applicable to salary bands, as follows:

Increase (%) 4.5 3.45 5.12 1.78

Page 62 of 69
Band 3 1 0 9

Note the complication: the percentage increase for band 8 is not specified/is missing. What is the amount
of increase in each Salary?

1.17.1.4 Example 4 – Re-calculate the diagonal elements of a numeric array

1.17.1.5 Example 5 – Return the powers of 2 of each element of a numeric vector/array

Ajay Askoolum
April 2009

References
2. 101 LINQ Samples: http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
3.
4. System Building with APL+Win, Ajay Askoolum, WileyBlackwell (7 Jul 2006), ISBN-10: 047003020

Page 63 of 69
101 LINQ to Objects Samples: From C# to APL+Win
Ajay Askoolum

Appendix A – Product & Customer Lists


The sample queries use the product and customer lists extensively; the way C# accesses these lists is shown
in Appendix B, the code listing. For APL+Win, C# returns the same lists as nested arrays.

The Product List

© ProductID ProductName Category UnitPrice UnitsInStock

œŒwi 'GetProductListEx'
1 Chai Beverages 18.00 39
2 Chang Beverages 19.00 17
3 Aniseed Syrup Condiments 10.00 13
4 Chef Anton's Cajun Seasoning Condiments 22.00 53
5 Chef Anton's Gumbo Mix Condiments 21.35 0
6 Grandma's Boysenberry Spread Condiments 25.00 120
7 Uncle Bob's Organic Dried Pears Produce 30.00 15
8 Northwoods Cranberry Sauce Condiments 40.00 6
9 Mishi Kobe Niku Meat/Poultry 97.00 29
10 Ikura Seafood 31.00 31
11 Queso Cabrales Dairy Products 21.00 22
12 Queso Manchego La Pastora Dairy Products 38.00 86
13 Konbu Seafood 6.00 24
14 Tofu Produce 23.25 35
15 Genen Shouyu Condiments 15.50 39
16 Pavlova Confections 17.45 29
17 Alice Mutton Meat/Poultry 39.00 0
18 Carnarvon Tigers Seafood 62.50 42
19 Teatime Chocolate Biscuits Confections 9.20 25
20 Sir Rodney's Marmalade Confections 81.00 40
21 Sir Rodney's Scones Confections 10.00 3
22 Gustaf's Knãckebr÷d Grains/Cereals 21.00 104
23 Tunnbr÷d Grains/Cereals 9.00 61
24 Guaranß Fantßstica Beverages 4.50 20
25 NuNuCa Nuÿ-Nougat-Creme Confections 14.00 76
26 Gumbãr Gummibãrchen Confections 31.23 15
27 Schoggi Schokolade Confections 43.90 49
28 R÷ssle Sauerkraut Produce 45.60 26
29 Thžringer Rostbratwurst Meat/Poultry 123.79 0
30 Nord-Ost Matjeshering Seafood 25.89 10
31 Gorgonzola Telino Dairy Products 12.50 0
32 Mascarpone Fabioli Dairy Products 32.00 9
33 Geitost Dairy Products 2.50 112
34 Sasquatch Ale Beverages 14.00 111
35 Steeleye Stout Beverages 18.00 20
36 Inlagd Sill Seafood 19.00 112
37 Gravad lax Seafood 26.00 11
38 C•te de Blaye Beverages 263.50 17
39 Chartreuse verte Beverages 18.00 69
40 Boston Crab Meat Seafood 18.40 123
41 Jack's New England Clam Chowder Seafood 9.65 85
42 Singaporean Hokkien Fried Mee Grains/Cereals 14.00 26
43 Ipoh Coffee Beverages 46.00 17
44 Gula Malacca Condiments 19.45 27
45 Rogede sild Seafood 9.50 5
46 Spegesild Seafood 12.00 95
47 Zaanse koeken Confections 9.50 36
48 Chocolade Confections 12.75 15
49 Maxilaku Confections 20.00 10
50 Valkoinen suklaa Confections 16.25 65
51 Manjimup Dried Apples Produce 53.00 20

© Ajay Askoolum
52 Filo Mix Grains/Cereals 7.00 38
53 Perth Pasties Meat/Poultry 32.80 0
54 Tourti²re Meat/Poultry 7.45 21
55 P¼t´ chinois Meat/Poultry 24.00 115
56 Gnocchi di nonna Alice Grains/Cereals 38.00 21
57 Ravioli Angelo Grains/Cereals 19.50 36
58 Escargots de Bourgogne Seafood 13.25 62
59 Raclette Courdavault Dairy Products 55.00 79
60 Camembert Pierrot Dairy Products 34.00 19
61 Sirop d'´rable Condiments 28.50 113
62 Tarte au sucre Confections 49.30 17
63 Vegie-spread Condiments 43.90 24
64 Wimmers gute Semmelkn÷del Grains/Cereals 33.25 22
65 Louisiana Fiery Hot Pepper Sauce Condiments 21.05 76
66 Louisiana Hot Spiced Okra Condiments 17.00 4
67 Laughing Lumberjack Lager Beverages 14.00 52
68 Scottish Longbreads Confections 12.50 6
69 Gudbrandsdalsost Dairy Products 36.00 26
70 Outback Lager Beverages 15.00 15
71 Flotemysost Dairy Products 21.50 26
72 Mozzarella di Giovanni Dairy Products 34.80 14
73 R÷d Kaviar Seafood 15.00 101
74 Longlife Tofu Produce 10.00 4
75 Rh÷nbrãu Klosterbier Beverages 7.75 125
76 Lakkalik÷÷ri Beverages 18.00 57
77 Original Frankfurter gržne Soÿe Condiments 13.00 32

The Customer List

The customer list is not tabular—ORDERS, its last element contains the orders for each customer and is
nested. I recommend that you use XML NOTEPAD to examine its structure and content; CUSTOMERS.XML,
an XML file, holds the structure.

Page 66 of 69
101 LINQ to Objects Samples: From C# to APL+Win
Ajay Askoolum
Index
Aggregate Operators Linq39 .............................................................36
Linq73............................................................. 51 Linq4 ...............................................................18
Linq74............................................................. 51 Linq40 .............................................................37
Linq75............................................................. 51 Linq41 .............................................................37
Linq77............................................................. 52 Linq42 .............................................................37
Linq78............................................................. 53 Linq44 .............................................................38
Linq79............................................................. 53 Linq45 .............................................................39
Linq80............................................................. 53 Linq46 .............................................................39
Linq81............................................................. 53 Linq47 .............................................................40
Linq82............................................................. 54 Linq48 .............................................................40
Linq83............................................................. 54 Linq49 .............................................................40
Linq84............................................................. 55 Linq5 ...............................................................18
Linq85............................................................. 55 Linq50 .............................................................41
Linq86............................................................. 55 Linq51 .............................................................41
Linq87............................................................. 56 Linq52 .............................................................42
Linq88............................................................. 56 Linq53 .............................................................42
Linq89............................................................. 56 Linq54 .............................................................43
Linq90............................................................. 57 Linq55 .............................................................43
Linq92............................................................. 57 Linq56 .............................................................44
Linq93............................................................. 57 Linq57 .............................................................44
APL Functions Linq58 .............................................................45
LCase ............................................................. 21 Linq59 .............................................................45
LINQ ................................................................. 9 Linq6 ...............................................................19
Linq1 ............................................................... 12 Linq60 .............................................................45
Linq10............................................................. 22 Linq61 .............................................................45
Linq100........................................................... 61 Linq62 .............................................................46
Linq101........................................................... 62 Linq63 .............................................................47
Linq11............................................................. 22 Linq63Ex .........................................................47
Linq12............................................................. 23 Linq64 .............................................................48
Linq13............................................................. 23 Linq65 .............................................................48
Linq14............................................................. 23 Linq66 .............................................................48
Linq15............................................................. 24 Linq67 .............................................................49
Linq16............................................................. 25 Linq68 .............................................................49
Linq17............................................................. 26 Linq7 ...............................................................20
Linq18............................................................. 26 Linq70 .............................................................50
Linq19............................................................. 28 Linq71 .............................................................50
Linq2 ............................................................... 16 Linq73 .............................................................51
Linq20............................................................. 28 Linq74 .............................................................51
Linq21............................................................. 28 Linq75 .............................................................51
Linq22............................................................. 29 Linq76 .............................................................52
Linq23............................................................. 29 Linq77 .............................................................52
Linq24............................................................. 30 Linq78 .............................................................53
Linq25............................................................. 31 Linq79 .............................................................53
Linq26............................................................. 31 Linq8 ...............................................................21
Linq27............................................................. 31 Linq80 .............................................................53
Linq28............................................................. 31 Linq81 .............................................................53
Linq29............................................................. 32 Linq82 .............................................................54
Linq3 ............................................................... 17 Linq83 .............................................................54
Linq30............................................................. 32 Linq84 .............................................................55
Linq31............................................................. 33 Linq85 .............................................................55
Linq32............................................................. 33 Linq86 .............................................................55
Linq33............................................................. 34 Linq87 .............................................................56
Linq34............................................................. 34 Linq88 .............................................................56
Linq35............................................................. 34 Linq89 .............................................................56
Linq36............................................................. 35 Linq9 ...............................................................21
Linq37............................................................. 35 Linq90 .............................................................57
Linq38............................................................. 36 Linq92 .............................................................57

© Ajay Askoolum
Linq93............................................................. 57 Linq38 .............................................................36
Linq94............................................................. 58 Linq39 .............................................................36
Linq95............................................................. 58 Linq40 .............................................................37
Linq96............................................................. 59 Linq41 .............................................................37
Linq97............................................................. 59 Linq42 .............................................................37
Linq97Ex......................................................... 59 Linq44 .............................................................38
Linq98............................................................. 60 Linq45 .............................................................39
Linq98Ex_IOTA .............................................. 61 Partitioning Operators
Linq99............................................................. 61 Linq20 .............................................................28
UCase............................................................. 21 Linq21 .............................................................28
Comparable Nested Array................................ 38 Linq22 .............................................................29
Conversion Operators Linq23 .............................................................29
Linq54............................................................. 43 Linq24 .............................................................30
Linq55............................................................. 43 Linq25 .............................................................31
Linq56............................................................. 44 Linq26 .............................................................31
Linq57............................................................. 44 Linq27 .............................................................31
Custom Sequence Operators Projection Operators
Linq94............................................................. 58 Linq10 .............................................................22
Linq95............................................................. 58 Linq11 .............................................................22
Linq96............................................................. 59 Linq12 .............................................................23
Linq97............................................................. 59 Linq13 .............................................................23
Linq98............................................................. 60 Linq14 .............................................................23
CUSTOMERS.XML ........................................ 7, 66 Linq15 .............................................................24
Element Operators Linq16 .............................................................25
Linq58............................................................. 45 Linq17 .............................................................26
Linq59............................................................. 45 Linq18 .............................................................26
Linq60............................................................. 45 Linq19 .............................................................28
Linq61............................................................. 45 Linq6 ...............................................................19
Linq62............................................................. 46 Linq7 ...............................................................20
Linq63............................................................. 47 Linq8 ...............................................................21
Linq64............................................................. 48 Linq9 ...............................................................21
Generation Operators Quantifiers
Linq65............................................................. 48 Linq67 .............................................................49
Linq66............................................................. 48 Linq68 .............................................................49
Operators Linq70 .............................................................50
Aggregate ....................................................... 50 Linq71 .............................................................50
Conversion ..................................................... 43 Linq76 .............................................................52
CustomSequence ........................................... 59 Query Execution
Element .......................................................... 44 Linq100 ...........................................................61
Generation...................................................... 48 Linq101 ...........................................................62
Grouping......................................................... 36 Linq99 .............................................................61
Miscellaneous................................................. 58 Restriction Operators
Partitioning...................................................... 28 Linq1 ...............................................................12
Projection........................................................ 19 Linq2 ...............................................................16
Quantifiers ...................................................... 49 Linq3 ...............................................................17
Restriction....................................................... 11 Linq4 ...............................................................18
Set .................................................................. 39 Linq5 ...............................................................18
Ordering Operators Set Operators
Linq28............................................................. 31 Linq46 .............................................................39
Linq29............................................................. 32 Linq47 .............................................................40
Linq30............................................................. 32 Linq48 .............................................................40
Linq31............................................................. 33 Linq49 .............................................................40
Linq32............................................................. 33 Linq50 .............................................................41
Linq33............................................................. 34 Linq51 .............................................................41
Linq34............................................................. 34 Linq52 .............................................................42
Linq35............................................................. 34 Linq53 .............................................................42
Linq36............................................................. 35 Signature ............................................................13
Linq37............................................................. 35 XML NOTEPAD ..............................................7, 66

Page 68 of 69
Code Listings
The remaining pages contains listing of EXPLORE.CS and DATA.CS from the Visual Studio 2008 project that
builds the COM DLL, LINQ.DLL, used throughout this article.

Page 69 of 69

S-ar putea să vă placă și