@Adám thanks a bunch for those. Unfortunately I only noticed after spending a few hours on it that the challenge was an optimization challenge, not code-golf, so I just scrapped everything I did >.>
SWEDAPL'18 is live streaming tomorrow! Please check http://swedapl.se for an updated agenda. To connect please visit https://zoom.us/j/677491573
The stream is scheduled to begin at 10am local time (9am BST).
I've gotten a request to cover ⎕R and ⎕S (Dyalog's regex operators). Any objections or better ideas?
OK then.
The most fundamental thing to understand about these two is that they are operators – not functions. Occasionally, their operator syntax has unexpected consequences, so it is important to remember this.
They are dyadic operators. The left operand is always a character scalar, vector, or vector of such.
The right operand may also be any of those, but can also be a function (any type; tacit, dfn or trad), and ⎕S can also take a scalar integer as right operand.
They then derive an ambivalent function which is can be named or applied to text.
Some of their behaviour can be modified with the ⍠ operator, but since operators can only take functions (or arrays) as operands, ⍠ will be acting on the derived function, not on ⎕R or ⎕S themselves. This may sound trivial, but you have to remember that you cannot make a case insensitive (more about that later) version of ⎕S with MyRegexMachine←⎕S⍠1, only MyRegexMachine←'something'⎕S'something else'⍠1.
Btw, pretty much anything about ⎕R and ⎕S applies to the QuadR and QuadS golfing languages too, as they are really just very thin covers for the corresponding operators.
Final note before we really start: The regex flavour is PCRE, which is well documented, so I won't go too much into details about it. It is summarised here and described in detail here.
⎕R (Replace) changes text in-place and returns the entire amended argument. ⎕S only returns the amended match(es). In most other aspects, they are identical, so when I speak of one, it applies to the other unless otherwise noted.
OK, the basic example is:
⍞←'and' ⎕R 'or' ⊢ 'Programming Puzzles and Code Golf'
However, the operands are not just simple text vectors, but rather regexes. For the left operand, that's just regular PCRE to find a match, but the right argument uses something that very much feels like regex, but in fact is a Dyalog-invented notation to indicate what you want the match replaced by.
The first such notational symbol is & which means the match itself, i.e. no change:
⎕←'(.)\1' ⎕S '&' ⊢ 'Programming Puzzles and Code Golf'
The left operand is just PCRE: . is any char, the parens gives it a number, and \1 repeats it, so it matches any sequence of two identical characters after each other.
A % in the right operand means the entire container (line or document) which contained the match:
So this returned a list of all lines which contained double letters.
We've earlier talked about how simple APL's "string" (i.e. character vector) model is. The only special character is the quote which you need to double. There's no escaping, rather you have to use …',(⎕UCS nn),'….
However, in the transformation string (that's what the right operand is called), you may also use some common escapes: \n and \r for newline and carriage return, and \x{nn} for any other Unicode character, where nn is in hex.
And because &, & and \ are special, you'll have to escape them too with a prefix backslash.
You may of course mix and match transformation strings as you please:
@Adám
┌──────────────────────────┬──────────────────────┐
│"Programming" has two "m"s│"Puzzles" has two "z"s│
└──────────────────────────┴──────────────────────┘
The above means that mm and zz begin 6 and 14 characters offset from the left. Notice that thes are offsets, not indices, so they are as indices in origin 0 (⎕IO←0).
1 is the length of the match:
⍞←'\w+' ⎕S 1 ⊢ 'Programming Puzzles and Code Golf'
@J.Sallé The amazing thing about the multiple patterns is that ⎕R and ⎕S step through the input letter by letter, and for each letter they look whether each pattern (from left to right) begins there.
You can of course also have multiple transformation patterns. This means that you can use a pattern to exclude from other patterns by placing the exclusion first, and replacing with the match (&):
@Cowsquack It makes each letter into its own match/transformation string, otherwise '&' '_' is the same as '&_'. I could have written ,¨'&_' but I prefer writing them separately when that's what I mean.
@H.PWiz As J.Sallé said. The vectorisation also works differently for numeric and text operands. Text goes pairwise, while numbers return the entire list for each.
@J.Sallé What do you mean by only the matches?
Btw, you can have one transformation string for each matching string, or a single transformation string for all the matching strings:
⍞←(,¨'aeiou') ⎕R (,¨'AEIOU') ⊢ 'Programming Puzzles and Code Golf'
I mentioned that you can use ⍠. The most commonly used option is case sensitivity, so it is the default option which means that you don't have to use the name-value pair ⍠'IC' 1 (Insensitive Case); ⍠1 is enough:
@EriktheOutgolfer While searching, as can be seen if you use ⎕R straight on a file instead of on raw data. You do this by supplying a tie number instead of a character array. If you try to use the same tie number for the output (the left argument) then you'll get a mangled file.
The right argument (which may of course be ignored) will be a namespace with a few members.
This namespace survives between matches for the entire time that the current ⎕R/⎕S call is ongoing, so you further populate the namespace and so use it to convey information from earlier matches to later matches.
The only names that are reserved (i.e. get overwritten each time your operand function is called) are:
@EriktheOutgolfer Well, the documentation warns against doing so.
BlockNum – same as 2
Pattern – the literal pattern which matched (i.e. not the match itself)
PatternNum – the origin 0 number of the above
Match – same as &
Offsets – first element is same as 0 but has additional elements corresponding to subpatterns which are those in parens, like (.) above.
Lengths – first element is same as 1 but has additional elements corresponding to subpatterns.
ReplaceMode – 0 for ⎕S and 1 for ⎕R
TextOnly – Boolean whether the result of the function must be a character vector (i.e. for ⎕R) or can be anything (i.e. for ⎕S).
The function can then do any computation necessary to determine its result, so you could even have it prompt the user for whether to replace this match or not (i.e. when implementing a "Replace All" button in an editor).
This of course renders ⎕R and ⎕S as powerful as Dyalog APL as a whole – they are both supersets and subsets of Dyalog APL!
And that concludes tonight's lesson. Sorry for going over time!