« first day (433 days earlier)      last day (2515 days later) » 

13:14
@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 >.>
 
2 hours later…
15:08
hm, looks like I won't be here when the cultivation starts, but may join before it ends
15:49
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).
Ven
Ven
16:10
Thanks
17:08
@Adám :D
17:30
Welcome to APL Cultivation!
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'
17:46
@Adám Programming Puzzles or Code Golf
I'm late D:
I assume R is for Replace. What is S for?
@H.PWiz Oops, sorry: Search.
Thanks
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'
17:51
@Adám
┌──┬──┐
│mm│zz│
└──┴──┘
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:
⎕←'(.)\1' ⎕S '%' ⊢ 'Programming' 'Puzzles' 'and' 'Code' 'Golf'
@Adám
┌───────────┬───────┐
│Programming│Puzzles│
└───────────┴───────┘
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:
⎕←'(.)\1' ⎕S '"%" has "&"' ⊢ 'Programming' 'Puzzles' 'and' 'Code' 'Golf'
@Adám
┌──────────────────────┬──────────────────┐
│"Programming" has "mm"│"Puzzles" has "zz"│
└──────────────────────┴──────────────────┘
You can also refer to the numbered groups with \n (or \(nn) for two-digit numbers):
⎕←'(.)\1' ⎕S '"%" has two "\1"s' ⊢ 'Programming' 'Puzzles' 'and' 'Code' 'Golf'
18:01
@Adám
┌──────────────────────────┬──────────────────────┐
│"Programming" has two "m"s│"Puzzles" has two "z"s│
└──────────────────────────┴──────────────────────┘
Finally, you can fold to upper or lowercase by inserting u or l immediately after the backslash (adding a backslash to & and %):
⎕←'(.)\1' ⎕S '"%" has 2 "\u1"s' ⊢ 'Programming' 'Puzzles' 'and' 'Code' 'Golf'
@Adám
┌────────────────────────┬────────────────────┐
│"Programming" has 2 "M"s│"Puzzles" has 2 "Z"s│
└────────────────────────┴────────────────────┘
I think I got most of it at least
This means that you can also use ⎕R to just fold case (like 819⌶):
⍞←'.'⎕R'\u&'⊢'Programming Puzzles and Code Golf'
@Adám PROGRAMMING PUZZLES AND CODE GOLF
18:05
@J.Sallé Anything unclear?
@Adám I'm reading through everything now
But I don't think so
In addition to using these text-based codes, ⎕S can also use a few numeric codes which then return numeric results.
0 is the offset from the start of the input of the start of the match:
⍞←'(.)\1'⎕S 0⊢'Programming Puzzles and Code Golf'
@Adám 6 14
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'
@Adám 11 7 3 4 4
18:12
⍞←'[[:upper:]]' ⎕S '"%" has "&" capitalised' ⊢ 'Programming' 'Puzzles' 'and' 'Code' 'Golf'
@Cowsquack  "Programming" has "P" capitalised  "Puzzles" has "P" capitalised  "Code" has "C" capitalised  "Golf" has "G" capitalised
\w is any word character, and + means one or more, so this matches whole words, and the result is a list of word lengths.
@DyalogAPL sorry if I'm jumping ahead, but is there a way to get how many uppercased characters there are in a string?
@Cowsquack You can format as code by using four leading spaces, and the bot will still react.
@Cowsquack You can e.g. match all uppercase letters and then tally the result:
⍞←≢'[[:upper:]]' ⎕S 0 ⊢ 'Programming Puzzles and Code Golf'
@Adám 4
18:17
I see
2 is the number of the block which had the match:
⍞←'(.)\1' ⎕S 2 ⊢ 'Programming' 'Puzzles' 'and' 'Code' 'Golf'
@Adám 0 1
So we can see that only strings 0 and 1 had double-letters. (Again, always origin 0.)
The last one, 3 is the pattern number, which brings us to an amazing feature of ⎕R and ⎕S; multiple simultaneous patterns:
⍞←'(.)\1' 'P' ⎕S 3 ⊢ 'Programming Puzzles and Code Golf'
@Adám 1 0 1 0
Again, the patterns are numbered in origin 0, so first we find a double-letter (mm), then a P, then a double-letter (zz) and then a P.
18:23
@Adám wait I didn't get that
You find it in the order you used as the left argument?
⍞←'P' '(.)\1' ⎕S 3 ⊢ 'Programming Puzzles and Code Golf'
@J.Sallé 0 1 0 1
@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.
Ah okay, I get it now
So pattern 0 is the first argument and 1 is the second
@J.Sallé Not argument: element of the left operand!
@Adám yes, that :p
18:26
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 (&):
⍞←' ' '\w' ⎕R (,¨'&' '_') ⊢ 'Programming Puzzles and Code Golf'
@Adám ___________ _______ ___ ____ ____
This replaced spaces with themselves, and word characters with underscores.
⍞←'P' '(.)\1' ⎕S 3 1 ⊢ 'Programming Puzzles and Code Golf'
@H.PWiz  0 1  1 2  0 1  1 2
⍞←'P' '(.)\1' ⎕S 3 0 ⊢ 'Programming Puzzles and Code Golf'
18:28
@H.PWiz  0 0  1 6  0 12  1 14
⍞←'\S' ⎕R '_' ⊢ 'Programming Puzzles and Code Golf'
@Cowsquack ___________ _______ ___ ____ ____
⍞←(,¨' ' '.') ⎕R (,¨'&' '_') ⊢ 'Programming Puzzles and Code Golf'
@Adám ___________ _______ ___ ____ ____
@Adám what does the do?
18:29
@Cowsquack concatenate each
But here, we replace spaces with themselves, and then any character – including spaces – with underscores.
@Adám how can I replace only the matches with something else? (underscores would be fine)
@J.Sallé why do you need it?
@Cowsquack I'm not sure
@Adám wow I didn't know ⎕R vectorizes like that
18:32
⍞←'P' '(.)\1' ⎕S 3 '&' ⊢ 'Programming Puzzles and Code Golf'
@H.PWiz DOMAIN ERROR
@H.PWiz I think you can either give ⎕S integer arguments or notation symbols, but not both
Yes, it seems so
@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.
ah I see
18:36
@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'
@Adám PrOgrAmmIng PUzzlEs And COdE GOlf
⍞←(,¨'aeiou') ⎕R '_' ⊢ 'Programming Puzzles and Code Golf'
@Adám Pr_gr_mm_ng P_zzl_s _nd C_d_ G_lf
@Adám nevermind, I noticed you'd already done that
But of course, you can't have multiple transformation strings for a single matching string:
⎕←'o'⎕R(,¨'AEIOU')⊢'Programming Puzzles and Code Golf'
18:41
@Adám
LENGTH ERROR
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:
⍞←'g'⎕R'_'⍠1⊢'Programming Puzzles and Code Golf'
@Adám Pro_rammin_ Puzzles and Code _olf
Notice that g matched both upper and lowercase Gs.
Another cool option is for ⎕S only: ⍠'OM' 1 (Overlapping Matches):
⎕←'[^aeiou]{3}'⎕S'&'⊢'Programming Puzzles and Code Golf'
@Adám
┌───┬───┬───┐
│ng │zzl│nd │
└───┴───┴───┘
@Adám regex should generally have that, but it usually doesn't :/
18:47
[^aeiou] means NOT any of these letters and {3} means exactly three of such.
⎕←'[^aeiou]{3}'⎕S'&'⍠'OM'1⊢'Programming Puzzles and Code Golf'
@Adám
┌───┬───┬───┬───┬───┐
│ng │g P│zzl│nd │d C│
└───┴───┴───┴───┴───┘
Notice how this matched g P even though its first two letters were already found in the first match.
⎕R cannot allow overlapping matches because that may lead to infinite substitution looping.
Sounds like a bad thing to happen :p
@Adám uh...how? I think there's another reason ⎕R can't allow overlapping matches
if you don't ⍣≡ it, then I'm not sure how it can be infinite at all
@EriktheOutgolfer E.g. 'x' ⎕R 'xx'⍠'OM' 1 would loop forever, no?
In xyz it would first replace x with xx to get xxyz then continue at the next character, which also matches, and makes xxxyz, etc.
18:52
@Adám but, for example 'x'⎕R'xx'⍠'OM' 1⊢'Alexey' I would expect it to return 'Alexxey', and the substitution only happens once, no?
@EriktheOutgolfer No, the definition of 'OM' is that the search continues with the next letter after the start of the match.
Let's see if we can squeeze in coverage of the most powerful feature of them all: that the right operand may be any monadic (or ambivalent) function.
@Adám sure, but do replaces happen while searching, not after?
@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:
@Adám hm, at least to me, it looks like that just asks for trouble...
Block – same as %
@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!
Do you want another lesson next week?
 
1 hour later…
20:12
Iff S is a subset and a superset of T, S is T. So they definitely are powerful. Totally forgot this was today.
@Zacharý Still, Dyalog APL clearly isn't ⎕R or ⎕S
(As in the abilities: they are "as powerful as Dyalog APL as a whole")
But nonetheless, if you have an entire language based upon RegEx (Retina), they are pretty useful.

« first day (433 days earlier)      last day (2515 days later) »