∆F is a function for Dyalog APL that interpret f-strings, a concise, yet powerful way to display multiline Unicode text and complex, often multidimensional expressions in an APL-friendly style. |
---|
Show/Hide Table of Contents
- Table of Contents
- Installing and Running ∆F in Dyalog APL
- Overview
- Displaying ∆F Help in APL
- ∆F Examples: A Primer
- Code Fields
- Text Fields and Space Fields
- Null Space Fields
- Code Fields (Continued)
- The Box Shortcut
- Box Mode
- Omega Shortcuts (Explicit)
- Referencing the F-string Itself
- The Format Shortcut
- The Shortcut for Numeric Commas
- Self-documenting Code fields (SDCFs)
- The Above Shortcut
- Omega Shortcuts (Implicit)
- Shortcuts With Individual Expressions
- A Shortcut for Dates and Times (Part I)
- A Shortcut for Dates and Times (Part II)
- The Quote Shortcut
- The Wrap Shortcut (Experimental)
- Precomputed F-strings with the DFN Option
- ∆F Syntax and Other Information
- Appendices
- Copyright
Show/Hide Installing and Running ∆F
- On Github, search for
"f-string-apl"
.- During test phrase, go to https://github.com/petermsiegel/f-string-apl.
- Copy the files ∆Fapl.dyalog and ∆F_Help.html into your current working directory (the one shown via
]cd
). - Then, from your Dyalog session (typically
#
or⎕SE
), enter:
]load ∆Fapl [-target=
myns]
- Each time it is called, the
]load
will create function ∆F and namespace ⍙Fapl in the active namespace (or myns).- ⍙Fapl contains utilities used by ∆F and, once
]load
ed, should not be moved. - ∆F may be relocated; it will refer to ⍙Fapl in its original location.
- ⍙Fapl contains utilities used by ∆F and, once
- If ∆F_Help.html is available at
]load
time, it will be copied into ⍙Fapl (or a message will note its absence).
- Each time it is called, the
Now, ∆F is available in the active namespace (or myns), along with ⍙Fapl.
]load ∆Fapl
(see above), ensuring that ∆F and ⍙Fapl are accessible from the current namespace.- Call
∆F
with the desired argument(s) and options.
Show/Hide Overview
Inspired by Python f-strings,¹ ∆F includes a variety of capabilities to make it easy to evaluate, format, annotate, and display related multidimensional information.² ∆F f-strings include:
-
The abstraction of 2-dimensional character fields, generated one-by-one from the user's specifications and data, then aligned and catenated into a single overall character matrix result;
-
Text fields, supporting multiline Unicode text within each field, with the sequence
`◇
(backtick + statement separator³) generating a newline (⎕UCS 13); -
Code fields, allowing users to evaluate and display APL arrays of any dimensionality, depth and type in the user environment, arrays passed as ∆F arguments, as well as arbitrary APL expressions based on full multi-statement dfn logic.⁴ Each Code field must return a value, simple or otherwise, which will be catenated with other fields and returned from ∆F;
Code fields also provide a number of concise, convenient extensions, such as:
-
Quoted strings in Code fields, with several quote styles:
- double-quotes
∆F '{"like this"}'
or∆F '{"on`◇""three""`◇lines"}
, - double angle quotation marks,⁵
∆F '{«with internal quotes like "this" or ''this''»}'
, not to mention - APL's tried-and-true embedded single-quotes,
∆F '{''shown ''''right'''' here''}'
.
- double-quotes
-
Simple shortcuts⁶ for
- formatting numeric arrays, $ (short for ⎕FMT):
∆F '{"F7.5" $ ?0 0}'
, - putting a box around a specific expression, `B:
∆F'{`B ⍳2 2}'
, - placing the output of one expression above another, %:
∆F'{"Pi"% ○1}'
, - formatting date and time expressions from APL timestamps (⎕TS) using `T (combining 1200⌶ and ⎕DT):
∆F'{"hh:mm:ss" `T ⎕TS}'
, - and more;
- formatting numeric arrays, $ (short for ⎕FMT):
-
Simple mechanisms for concisely formatting and displaying data from
- user arrays or arbitrary code:
tempC←10 110 40
∆F'{tempC}'
or∆F'{ {⍵<100: 32+9×⍵÷5 ◇ "(too hot)"}¨tempC }'
, - arguments to ∆F that follow the format string:
∆F'{32+`⍵1×9÷5}' (10 110 40)
,
where`⍵1
is a shortcut for(⍵⊃⍨1+⎕IO)
(here10 110 40
), - and more;
- user arrays or arbitrary code:
-
-
Space fields, providing a simple mechanism both for separating adjacent Text fields and inserting (rectangular) blocks of any number of spaces between any two fields, where needed;
- one space:
{ }
; five spaces:{ }
; or even, zero spaces:{}
; - 1000 spaces? Use a code field instead:
{1000⍴""}
.
- one space:
-
Multiline (matrix) output built up field-by-field, left-to-right, from values and expressions in the calling environment or arguments to ∆F;
- After all fields are generated, they are concatenated (after appropriate vertical alignment) to form a single character matrix: the return value from ∆F. (See the examples below).
∆F is designed for ease of use, ad hoc debugging, fine-grained formatting and informal user interaction,⁷ built using Dyalog functions and operators.
Recap: The Three Field Types
Field Type | Syntax | Examples | Displaying |
---|---|---|---|
Text | Unicode text | abc`◇def |
2-D Text |
Code | { dfn code plus} |
{(32+9×÷∘5)degC} {↑"one" "two"} |
Arbitrary APL expressions via dfns |
Space | { ␠ ␠ ␠} |
{ } {} |
Spacing & separation |
Notes
¹ ∆F is inspired by Python f-strings, short for "formatted string literals", but designed for APL's multi-dimensional worldview. Python introduced f-strings in 2016. ∆F f-strings and Python's are not compatible. |
² Throughout this documentation, an index origin of zero (⎕IO←0 ) is assumed. Since Code fields inherit the index origin and other system variables from the environment in which ∆F is called, your own examples will work as you expect. |
³ In this document, we use the symbol ◇ (⎕UCS 9671 ) to represent the APL statement separator (⎕UCS 8900 ), since the latter is displayed in some browsers as a hard-to-read glyph. ∆F will recognize `◇ with either glyph. |
⁴ An ∆F f-string— including any Code fields— is limited to a single, possibly very long, character vector. |
⁵ Double angle quotation marks « » (guillemets) are Unicode chars ⎕UCS 171 187 . |
⁶ Details on all the shortcuts are provided later in this document. See Code Field Shortcuts. |
⁷ As a prototype, ∆F is relatively slow, using an APL recursive scan to analyze the f-string. See the DFN option (below) for a way to speed up frequently used f-strings. |
👉 To display this HELP information, type: ∆F⍨ 'help'
.
Show/Hide Examples: A Primer
Before providing information on ∆F syntax and other details, let's start with some examples…
First, let's set some context for the examples. (You can set these however you want.)
⎕IO ⎕ML← 0 1
Here are Code fields with simple variables.
name← 'Fred' ◇ age← 43
∆F 'The patient''s name is {name}. {name} is {age} years old.'
The patient's name is Fred. Fred is 43 years old.
Code fields can contain arbitrary expressions. With default options, ∆F always returns a single character matrix. Here ∆F returns a matrix with 2 rows and 32 columns.
tempC← ⍪35 85
⍴⎕← ∆F 'The temperature is {tempC}{2 2⍴"°C"} or {32+tempC×9÷5}{2 2⍴"°F"}'
The temperature is 35°C or 95°F.
85°C 185°F
2 32
Here, we assign the f-string to an APL variable, then call ∆F twice!
⎕RL← 2342342 ⍝ ⎕RL: Ensure our random #s aren't random!
names← 'Mary' 'Jack' 'Tony' ◇ prize← 1000
f← 'Customer {names⊃⍨ ?≢names} wins £{?prize}!'
∆F f
Customer Jack wins £80!
∆F f
Customer Jack wins £230!
Isn't Jack lucky, winning twice in a row!
Below, we have some multi-line Text fields separated by non-null Space fields.
- The backtick is our "escape" character.
- The sequence `◇ generates a new line in the current text field.
- Each Space field
{ }
in the next example contains one space within its braces. It produces a matrix a single space wide with as many rows as required to catenate it with adjacent fields.
A Space field is useful here because each multi-line field is built in its own rectangular space.
∆F 'This`◇is`◇an`◇example{ }Of`◇multi-line{ }Text`◇Fields'
This Of Text
is multi-line Fields
an
example
Two adjacent Text fields can be separated by a null Space field {}
,
for example when at least one field contains multiline input that you
want formatted separately from others, keeping each field in is own rectangular space:
⍝ Extra space here ↓
∆F 'Cat`◇Elephant `◇Mouse{}Felix`◇Dumbo`◇Mickey'
Cat Felix
Elephant Dumbo
Mouse Mickey
In the above example, we added an extra space after the longest animal name, Elephant, so it wouldn't run into the next word, Dumbo.
But wait! There's an easier way!
Here, you surely want the lefthand field to be guaranteed to have a space after each word without fiddling; a Space field with at least one space will solve the problem:
⍝ ↓↓↓
∆F 'Cat`◇Elephant`◇Mouse{ }Felix`◇Dumbo`◇Mickey'
Cat Felix
Elephant Dumbo
Mouse Mickey
And this is the same example, but with two Code fields separated
by a Text field with a single space. (We could have used a Space field { }
here as well.)
∆F '{↑"Cat" "Elephant" "Mouse"} {↑"Felix" "Dumbo" "Mickey"}'
Cat Felix
Elephant Dumbo
Mouse Mickey
Here's a similar example with double quote-delimited strings in Code fields with
the newline sequence, `◇
:
∆F '{"This`◇is`◇an`◇example"} {"Of`◇Multi-line"} {"Strings`◇in`◇Code`◇Fields"}'
This Of Strings
is Multi-line in
an Code
example Fields
Here is some multiline data we'll add to our Code fields, using APL mix ↑
to generate multiline objects (matrices).
fNm← 'John' 'Mary' 'Ted'
lNm← 'Smith' 'Jones' 'Templeton'
addr← '24 Mulberry Ln' '22 Smith St' '12 High St'
∆F '{↑fNm} {↑lNm} {↑addr}'
John Smith 24 Mulberry Ln
Mary Jones 22 Smith St
Ted Templeton 12 High St
Here's a slightly more interesting code expression, using the shortcut $
(i.e. Dyalog's ⎕FMT
)
to round Centigrade numbers to the nearest whole degree and Fahrenheit numbers to the nearest tenth of a degree.
(We could have used 0⍕⍪
and 1⍕⍪
, of course.)
C← 11.3 29.55 59.99
∆F 'The temperature is {"I2" $ C}°C or {"F5.1"$ 32+9×C÷5}°F'
The temperature is 11°C or 52.3°F
30 85.2
60 140.0
Here we place boxes around key Code fields in this same example to introduce the Box shortcut `B
.
C← 11.3 29.55 59.99
∆F '`◇The temperature is {`B "I2" $ C}`◇°C or {`B "F5.1" $ 32+9×C÷5}`◇°F'
┌──┐ ┌─────┐
The temperature is │11│°C or │ 52.3│°F
│30│ │ 85.2│
│60│ │140.0│
└──┘ └─────┘
What if you want to place a box around every Code, Text, and Space field? We can just use the Box mode option!
While we can't place boxes around text (or space) fields using `B
,
we can place a box around each field regardless of type. by setting Box mode (∆F's
third option) to 1
, e.g. setting ∆F's left argument to 0 0 1
:
C← 11.3 29.55 59.99
⍝ ↓¯¯¯ Box mode
0 0 1 ∆F '`◇The temperature is {"I2" $ C}`◇°C or {"F5.1" $ 32+9×C÷5}`◇°F'
┌───────────────────┬──┬──────┬─────┬──┐
│ │11│ │ 52.3│ │
│The temperature is │30│°C or │ 85.2│°F│
│ │60│ │140.0│ │
└───────────────────┴──┴──────┴─────┴──┘
We said you could place a box around every field, but there's an exception.
Null Space fields {}
, i.e. 0-width Space fields, are discarded once they've done their work of separating adjacent fields (typically Text fields), so they won't be placed in boxes. Try this expression on your own:
0 0 1 ∆F 'abc{}def{}{}ghi{""}jkl{ }mno'
Peek
┌───┬───┬───┬┬───┬─┬───┐
│abc│def│ghi││jkl│ │mno│
└───┴───┴───┴┴───┴─┴───┘
In contrast, Code fields that return null values (like {""}
above) will be displayed!
Referencing ∆F arguments after the f-string: Omega shortcut expressions like
`⍵1
.
The expression `⍵1
is equivalent to (⍵⊃⍨ 1+⎕IO)
, selecting the first argument after the f-string. Similarly, `⍵99
would select (⍵⊃⍨99+⎕IO)
.
We will use `⍵1
here, both with shortcuts and an externally defined
function C2F
, that converts Centigrade to Fahrenheit.
A bit further below, we discuss bare `⍵
(i.e. without an appended non-negative integer).
C2F← 32+9×÷∘5
∆F 'The temperature is {"I2" $ `⍵1}°C or {"F5.1" $ C2F `⍵1}°F' (11 15 20)
The temperature is 11°C or 51.8°F
15 59.0
20 68.0
The expression `⍵0
always refers to the f-string itself.¹ Try this yourself:²
∆F 'Our string {`⍵0↓} is {≢`⍵0} characters'
Peek
∆F 'Our string {`⍵0↓} is {≢`⍵0} characters'
Our string `⍵0↓ is 38 characters
Our string {`⍵0↓} is {≢`⍵0} characters
Notes
¹ `⍵0 refers to the f-string independent of the the number of elements in the right argument to ∆F (effectively, ⊆⍵ ). |
² We explain the ↓ before the closing brace } under Self-documenting Code fields below. |
Let's add commas to some very large numbers using the ⎕FMT shortcut
$
.
We can use Dyalog's built-in formatting specifier "C" with shortcut $
to add appropriate commas to the temperatures!
⍝ The temperature of the sun at its core in degrees C.
sun_core← 15E6 ⍝ 15000000 is a bit hard to parse!
∆F 'The sun''s core is at {"CI10" $ sun_core}°C or {"CI10" $ C2F sun_core}°F'
The sun's core is at 15,000,000°C or 27,000,032°F
The [Numeric] Commas shortcut `C
adds commas every 3 digits (from the right) to one or more numbers or numeric strings.¹ It has an advantage over the $
(Dyalog's ⎕FMT
) specifier: it doesn't require you to guesstimate field widths.
Note
¹ Typically, each number or numeric string presented to `C will represent an integer, but if a real number is presented, only the integer part will have commas added. |
Let's use the `C
shortcut to add the commas to the temperatures!
sun_core← 15E6 ⍝ 15000000 is a bit hard to parse!
∆F 'The sun''s core is at {`C sun_core}°C or {`C C2F sun_core}°F.'
The sun's core is at 15,000,000°C or 27,000,032°F.
Cool! OK, not literally.
And for a bit of a twist, let's display either degrees Centigrade
or Fahrenheit under user control (1
=> F, 0
=> C). Here, we establish
the format-string sunFC
first, then pass it to ∆F with an additional argument.
sunFC← 'The sun''s core is at {`C C2F⍣`⍵1⊢ sun_core}°{ `⍵1⊃ "CF"}.'
∆F sunFC 1
The sun's core is at 27,000,032°F.
∆F sunFC 0
The sun's core is at 15,000,000°C.
Now, let's move on to Self-documenting Code fields.
Self-documenting Code fields are a useful debugging tool.
What's an SDCF? An SDCF¹ allows whatever source code is in a Code Field to be automatically displayed literally along with the result of evaluating that code.
The source code for a Code field can automatically be shown in ∆F's output—
- to the left of the result of evaluating that code; or,
- centered above the result of evaluating that code.
All you need do is enter
- a right arrow
→
for a horizontal SDCF, or - a down arrow
↓
(or%
²) for a vertical SDCF,
as the last non-space character in the Code field, before the final right brace.
Notes
¹ Our SDCFs are based on Python's single type of self-documenting expressions in f-strings, but work somewhat differently. SDCFs are used only in Code fields (duh). |
² % is the same glyph as for the Above shortcut, % or `A , discussed in the next section. |
Here's an example of a horizontal SDCF, i.e. using →
:
name←'John Smith' ◇ age← 34
∆F 'Current employee: {name→}, {age→}.'
Current employee: name→John Smith, age→34.
As a useful formatting feature, whatever spaces are just before or after the symbol → or ↓ are preserved verbatim in the output.
Here's an example with such spaces: see how the spaces adjacent to the symbol →
are mirrored in the output!
name←'John Smith' ◇ age← 34
∆F 'Current employee: {name → }, {age→ }.'
Current employee: name → John Smith, age→ 34.
Now, let's look at an example of a vertical SDCF, i.e. using ↓
:
name←'John Smith' ◇ age← 34
∆F 'Current employee: {name↓} {age↓}.'
Current employee: name↓ age↓.
John Smith 34
To make it easier to see, here's the same result, but with a box around each field (using the Box option 0 0 1
).
⍝ Box all fields
0 0 1 ∆F 'Current employee: {name↓} {age↓}.'
┌──────────────────┬──────────┬─┬────┬─┐
│Current employee: │ name↓ │ │age↓│.│
│ │John Smith│ │ 34 │ │
└──────────────────┴──────────┴─┴────┴─┘
A cut above the rest…
Here's a useful feature. Let's use the shortcut %
to display one expression centered above another; it's called Above and can also be expressed as `A
. Remember, `⍵1
designates the first argument after the f-string itself, and `⍵2
the second.
∆F '{"Employee" % ⍪`⍵1} {"Age" % ⍪`⍵2}' ('John Smith' 'Mary Jones')(29 23)
Employee Age
John Smith 29
Mary Jones 23
The next best thing: the use of
`⍵
in Code field expressions…
We said we'd present the use of Omega shortcuts with implicit indices `⍵
in Code fields. The expression `⍵
selects the next element of the right argument ⍵
to ∆F, defaulting to `⍵1
when first encountered, i.e. if there are no `⍵
elements (explicit or implicit) to the left in the entire f-string. If there is any such expression (e.g. `⍵5
), then `⍵
points to the element after that one (e.g. `⍵6
). If the item to the left is `⍵
, then we simply increment the index by 1
from that one.
Let's try an example. Here, we display arbitrary 2-dimensional expressions, one above the other.
`⍵
refers to the next argument in sequence, left to right, starting with `⍵1
, the first, i.e. (⍵⊃⍨ 1+⎕IO)
. So, from left to right `⍵
is `⍵1
, `⍵2
, and `⍵3
. Easy peasy.
∆F '{(⍳2⍴`⍵) % (⍳2⍴`⍵) % (⍳2⍴`⍵)}' 1 2 3
0 0
0 0 0 1
1 0 1 1
0 0 0 1 0 2
1 0 1 1 1 2
2 0 2 1 2 2
Let's demonstrate here the equivalence of the implicitly and explicitly indexed Omega expressions!
a← ∆F '{(⍳2⍴`⍵) % (⍳2⍴`⍵) % (⍳2⍴`⍵)}' 1 2 3 ⍝ Implicit Omega expressions
b← ∆F '{(⍳2⍴`⍵1) % (⍳2⍴`⍵2) % (⍳2⍴`⍵3)}' 1 2 3 ⍝ Explicit Omega expressions
a ≡ b ⍝ Are they the same?
1 ⍝ Yes!
Shortcuts often make sense with individual expressions, not just entire Code fields. They can be manipulated like ordinary APL functions; since they are just that -- ordinary APL functions -- under the covers. Here, we display one boxed value above the other.
∆F '{(`B ⍳`⍵1) % `B ⍳`⍵2}' (2 2)(3 3)
┌───┬───┐
│0 0│0 1│
├───┼───┤
│1 0│1 1│
└───┴───┘
┌───┬───┬───┐
│0 0│0 1│0 2│
├───┼───┼───┤
│1 0│1 1│1 2│
├───┼───┼───┤
│2 0│2 1│2 2│
└───┴───┴───┘
While not for the faint of heart, the expression above can be recast as this somewhat hard to read alternative:
∆F '{%/ `B∘⍳¨ `⍵1 `⍵2}' (2 2)(3 3)
There are loads of other examples to discover.
∆F supports a simple Date-Time shortcut `T
built from 1200⌶ and ⎕DT. It takes one or more Dyalog ⎕TS
-format timestamps as the right argument and a date-time specification as the (optional) left argument. Trailing elements of a timestamp may be omitted (they will each be treated as 0
in the specification string).
Let's look at the use of the `T
shortcut to show the current time (now).
∆F 'It is now {"t:mm pp" `T ⎕TS}.'
It is now 8:08 am.
Of course, the time displayed in practice will be the actual current time.
Here's a fancier example (the power is in 1200⌶
and ⎕DT
).
(We've added the truncated timestamp 2025 01 01
right into the f-string.)
∆F '{ "D MMM YYYY ''was a'' Dddd."`T 2025 01 01}'
1 JAN 2025 was a Wednesday.
If it bothers you to use `T
for a date-only expression,
you can use `D
, which means exactly the same thing.
∆F '{ "D MMM YYYY ''was a'' Dddd." `D 2025 01 02}'
2 JAN 2025 was a Thursday.
Here, we'll pass the time stamp via a single omega
expression (hence it is in parentheses): `⍵1
.
∆F '{ "D Mmm YYYY ''was a'' Dddd." `T `⍵1}' (2025 1 21)
21 Jan 2025 was a Tuesday.
We could also pass the time stamp via a sequence of omega
expressions: `⍵ `⍵ `⍵
.
This is equivalent to the slightly verbose
expression: `⍵1 `⍵2 `⍵3
.
∆F '{ "D Mmm YYYY ''was a'' Dddd." `T `⍵ `⍵ `⍵}' 2025 1 21
21 Jan 2025 was a Tuesday.
Placing quotes around string elements of an array.
The Quote shortcut `Q
recursively scans its right argument, matching rows of character arrays, character vectors, and character scalars, doubling internal single quotes and
placing single quotes around the items found.¹ Non-character data is returned as is. This is useful, for example, when you wish to clearly distinguish character from numeric data.
Note
¹ If a multidimensional character array is found, its rows are quoted; if a character vector, it is quoted in toto; else, each character scalar is quoted in isolation. |
Let's look at a couple of simple examples:
First, let's use the `Q
shortcut to place quotes around the simple character
arrays in its right argument, ⍵
. This is useful when you want to distinguish between character output that might include numbers and actual numeric output.
∆F '{`Q 1 2 "three" 4 5 (⍪1 "2") (⍪"cats" "dogs")}'
1 2 'three' 4 5 1 'cats'
'2' 'dogs'
And here's an example with a simple, mixed vector (i.e. with character and numeric scalars only). First, we display an object without using the Quote shortcut. Are you sure which elements are numeric and which character scalars?
∆F '{1 2 "3" 4 "5"}'
1 2 3 4 5
Now, we show it with the Quote shortcut. Voilà, quotes appear around the character digits, but not the actual numbers!
∆F '{`Q 1 2 "3" 4 "5"}'
1 2 '3' 4 '5'
Wrapping results in left and right decorators
Here we make a quick mention of the experimental shortcut Wrap¹ `W
which is used when you want a decorator string that is placed immediately to the left or right of each row of simple objects in the right argument, ⍵
.
- The decorators are in
⍺
, the left argument to Wrap: the left decorator,0⊃2⍴⍺
, and the right decorator,1⊃2⍴⍺
, with⍺
defaulting to a single quote. - If you need to omit one or the other decorator, simply make it a null string
""
or a zilde⍬
.
Note
¹ Wrap differs from the Quote shortcut `Q , which puts quotes only around the character arrays in ⍵ . For more, see Wrap (`W ) Details below. |
Here are two simple examples.
In the first, we place "°C"
after [a] each row of a table ⍪`⍵2
, or [b] after each simple vector in ,¨`⍵2
. We indicate that is no left decorator here
using ""
or ⍬
, as here.
⍝ ... [a] ... .... [b] ....
∆F '{ `⍵1 `W ⍪`⍵2 } ...{ `⍵1 `W ,¨`⍵2 }' (⍬ '°C')(18 22 33)
18°C ... 18°C 22°C 33°C
22°C
33°C
In this next example, we place brackets around the lines of each simple array in a complex array.
∆F '{"[]" `W ("cats")(⍳2 2 1)(2 2⍴⍳4)(3 3⍴⎕A) }'
[cats] [0 0 0] [0 1] [ABC]
[0 1 0] [2 3] [DEF]
[GHI]
[1 0 0]
[1 1 0]
The default returned from ∆F is always (on success) a character matrix. That can be expressed schematically via expression (a), shown here¹:
(a) 0 ∆F…
However, if the initial option (DFN) is 1
, as in (b),
(b) 1 ∆F…
then ∆F returns a dfn that, when called later, will return precisely the same character expression as for (a).² This is most useful when you are making repeated use of an f-string, since the overhead for analyzing the f-string contents once will be amortized over all the calls.
Notes
¹ ∆F's default initial option (left argument) is 0 , so 0 ∆F… and ∆F… are equivalent. We discuss all the options to ∆F later in this document. |
² This assumes the resulting dfn is called with the same arguments in the same calling environment in the same state. |
Let's explore an example where getting the best performance for a heavily used ∆F string is important.
First, let's grab cmpx
, so we can compare the performance…
'cmpx' ⎕CY 'dfns'
Now, let's proceed. Here's the code:
C← 11 30 60
⍝ Here's our ∆F String t
t←'The temperature is {"I2" $ C}°C or {"F5.1" $ F← 32+9×C÷5}°F'
⍝ Let's precompute a dfn T, given ∆F String t.
⍝ It has everything needed to generate the output
⍝ (except any external variables or additional arguments referenced).
T←1 ∆F t
⍝ Compare the performance of the two formats…
⍝ The precomputed version is about 17 times faster, in this run.
cmpx '∆F t' 'T ⍬'
∆F t → 1.7E¯4 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
T ⍬ → 1.0E¯5 | -94% ⎕⎕
Before we get to syntax and other information…
Finally, we want to show you that the dfn returned from 1…∆F…
can retrieve argument(s) passed on the right side of ∆F, using the very same omega shortcut expressions (`⍵1
, etc.) as described above.¹
Note
¹ The dfn returned from 1…∆F… includes the original f-string text used to generate it. The f-string is available as `⍵0 , as expected. |
As a variation on the example above, let's share the centigrade value,
not as a variable, but pass it as the first argument to ∆F (i.e. `⍵1`
).
t←'The temperature is {"I2" $ `⍵1}°C or {"F5.1" $ F← 32+9×`⍵1÷5}°F'
T← 1 ∆F t
∆F t 35
The temperature is 35°C or 95.0°F
T 35
The temperature is 35°C or 95.0°F
cmpx '∆F t 35' 'T 35'
∆F t 35 → 1.7E¯4 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
T 35 → 8.9E¯6 | -95% ⎕⎕
Below, we summarize key information you've already gleaned from the examples.
Show/Hide Syntax Info
Call Syntax | Description |
---|---|
∆F f-string | Display an f-string; use the default options. The string may reference objects in the environment or in the string itself. Returns a character matrix. |
∆F f-string args | Display an f-string; use the default options. Arguments presented may be referred to in the f-string. Returns a character matrix. |
options ∆F f-string [args] | Display an f-string; control the result with options specified (see below). |
If DFN (see below) is 0 or omitted, returns a character matrix. |
|
If DFN is 1 , returns a dfn that will display such a matrix (given an identical system state). |
|
'help' ∆F ' ' | Display help info and examples for ∆F. The f-string is not examined. |
∆F⍨'help' | Display help info and examples for ∆F. |
Element | Description |
---|---|
f-string | a format string, a single character vector. |
args | elements of ⍵ after the f-string, each of which can be accessed in the f-string via an Omega shortcut (`⍵𝑑𝑑 , etc.) or an ordinary dfn ⍵ expression. |
options: mode | options← [ [ 0 [ 0 [ 0 [ 0 ] ] ] ] | 'help' ] |
options[0]: DFN output mode |
If 1 : ∆F returns a dfn, which (upon execution) produces the same output as the default mode.If 0 (default): ∆F returns a char. matrix. |
options[1]: DBG (debug) mode |
If 1 : Renders newline characters from `◇ as the visible  character. Displays the source code that the f-string actually generates; if DFN is also 1 , this will include the embedded f-string source (accessed as `⍵0 ). After the source code is displayed, it will be executed or converted to a dfn and returned (see the DFN option above).If 0 (default): Newline characters from `◇ are rendered normally as carriage returns, ⎕UCS 13 ; the DFN source code is not displayed. |
options[2]: BOX mode |
If 1 : Each field (except a null Text field) is boxed separately.If 0 (default): Nothing is boxed automatically. Any Code field expression may be explicitly boxed using the Box shortcut, `B .Note: BOX mode can be used both with DFN and default output mode. |
options[3]: INLINE mode |
If 1 and the DFN option is set: The code for each internal support function used is included in the dfn result; no reference to namespace ⍙Fapl will be made during the execution of that dfn.If 0 (default): Whenever ∆F or a dfn generated by it is executed, it makes calls to library routines in the namespace ⍙Fapl, created during the ]load ∆Fapl process.Note: This option is experimental and may simply disappear one day. |
'help' | If 'help' is specified, this amazing documentation is displayed. |
result | If 0=⊃options , the result is always a character matrix.If 1=⊃options , the result is a dfn that, when executed in the same environment with the same arguments, generates that same character matrix. Note: If an error is signalled, no result is returned. |
- If the left argument
⍺
is omitted, the options default to4⍴0
. - If the left argument
⍺
is a simple integer vector or scalar, or an empty numeric vector⍬
, the options are4↑⍺
; subsequent elements are ignored; - If the left argument
⍺
starts with'help'
(case ignored), this help information is displayed. In this case only, the right argument to ∆F is ignored. - Otherwise, an error is signaled.
- Unless the DFN option is selected, ∆F always returns a character matrix of at least one row and zero columns,
1 0⍴0
, on success. If the 'help' option is specified, ∆F displays this information, returning1 0⍴0
. - If the DFN option is selected, ∆F always returns a standard Dyalog dfn on success.
- On failure of any sort, an informative APL error is signaled.
The first element in the right arg to ∆F is a character vector, an f-string, which contains one or more Text fields, Code fields, and Space fields in any combination.
- Text fields consist of simple text, which may include any Unicode characters desired, including newlines. Newlines (actually, carriage returns,
⎕UCS 13
) are normally entered via the sequence`◇
. Additionally, literal curly braces can be added via`{
and`}
, so they are distinct from the simple curly braces used to begin and end Code fields and Space Fields. Finally, a single backtick escape can be entered into a Text field by entering two such characters together``
.- If ∆F is called with an empty string,
∆F ''
, it is interpreted as containing a single 0-length Text field, returning a matrix of shape1 0
.
- If ∆F is called with an empty string,
- Code fields are run-time evaluated expressions enclosed within
simple, unescaped curly braces
{}
, i.e. those not preceded by a back-tick (see the previous paragraph). Code fields are, under the covers, Dyalog dfns with some extras. For escape sequences, see Escape Sequences below. - Space fields appear to be a special, degenerate, form of Code fields, consisting of a single pair of simple (unescaped) curly braces
{}
with zero or more spaces in between.- A Space field with zero spaces is a null Space field; while it may separate any other fields, its typical use is to separate two adjacent Text fields.
- Between fields, ∆F adds no automatic spaces; that spacing is under user control.
∆F Code fields may contain various shortcuts, intended to be concise and expressive tools for common tasks. Shortcuts are valid in Code fields only outside Quoted strings.
Shortcuts include:
Shortcut | Name | Meaning |
---|---|---|
`A, % | Above | [⍺] % ⍵ . Centers array ⍺ above array ⍵ . If omitted, ⍺←'' , i.e. a blank line. |
`B | Box | `B ⍵ . Places ⍵ in a box. ⍵ is any array. |
`C | Commas | `C ⍵ . Adds commas to ⍵ after every 3rd digit of the integer part of ⍵ , right-to-left. ⍵ is a vector of num strings or numbers. |
`D | Date-Time¹ | Synonym for `T. |
`F, $ | ⎕FMT | [⍺] $ ⍵ . Short for [⍺] ⎕FMT ⍵ . (See APL documentation). |
`Q | Quote | [⍺]`Q ⍵ . Recursively scans ⍵ , putting char. vectors, scalars, and rows of higher-dimensional strings in APL quotes, leaving other elements as is. If omitted, ⍺←'''' . |
`T | Date-Time¹ | [⍺]`T ⍵ . Displays timestamp(s) ⍵ according to date-time template ⍺ . ⍵ is one or more APL timestamps ⎕TS . ⍺ is a date-time template in 1200⌶ format. If omitted, ⍺← 'YYYY-MM-DD hh:mm:ss' . |
`W | Wrap EXPERIMENTAL! | [⍺]`W ⍵ . Wraps the rows of simple arrays in ⍵ in decorators 0⊃2⍴⍺ (on the left) and 1⊃2⍴⍺ (on the right). If omitted, ⍺←'''' . See details below. |
`⍵𝑑𝑑, ⍹𝑑𝑑 | Omega Shortcut (EXPLICIT) | A shortcut of the form `⍵𝑑𝑑 (or ⍹𝑑𝑑 ), to access the 𝑑𝑑 th element of ⍵ , i.e. (⍵⊃⍨ 𝑑𝑑+⎕IO) . See details below. |
`⍵, ⍹ | Omega Shortcut (IMPLICIT) | A shortcut of the form `⍵ (or ⍹ ), to access the next element of ⍵ . See details below. |
→ ↓ or % |
Self-documenting Code Fields (SDCFs) | → /↓ (synonym: % ) signal that the source code for the Code field appears before/above its value. Surrounding blanks are significant. See SDCFs in Examples for details. |
Note
¹ The syntax for the Date-Time specifications (left arg) can be found in the Dyalog documentation under 1200⌶. For the curious, here's the code actually used by the Date-Time shortcut: {⍺←'YYYY-MM-DD hh:mm:ss' ◇ ∊⍣(1=≡⍵)⊢ ⍺(1200⌶)⊢ 1⎕DT ⊆⍵} . |
∆F Text fields and Quoted strings in Code fields may include
a small number of escape sequences, beginning with the backtick `
.
Some sequences are valid in Text fields only, but not in Quoted strings:
Escape Sequence | What It Inserts | Description | Where Valid |
---|---|---|---|
`◇ | newline | ⎕UCS 13 | Both |
`` | ` | backtick | Both |
`{ | { | left brace | Text fields only |
`} | } | right brace | Text fields only |
Other instances of the backtick character in Text fields or Quoted strings in Code fields will be treated literally, i.e. sometimes a backtick is just a backtick.
As mentioned in the introduction, Quoted strings in Code fields allow several delimiting quote styles:
- double-quotes
∆F '{"like «this» one"}'
or∆F '{"like ''this'' one."}'
, - double angle quotation marks,
∆F '{«like "this" or ''this''.»}'
,
as well as - APL's tried-and-true embedded single-quotes,
∆F '{''shown like ''''this'''', "this" or «this».''}'
.
If you wish to include a traditional delimiting quote ('
or "
) or the closing quote of a quote pair («
»
) within the Quoted string, you must double it.
You may not use an escape sequence (e.g. `"
) for this purpose.¹
Closing Quote | Example | Result |
---|---|---|
" |
∆F '{"like ""this"" example"}' |
like "this" example |
» |
∆F '{«or «this»» one»}' |
or «this» one |
' |
∆F '{''or ''''this'''' one''}' |
or 'this' one |
Note that the opening quote «
is treated as an ordinary character within the string. The clumsiness of the standard single quote '
examples is due to the fact that the single quote is the required delimiter for the outermost (APL-level) string.
Note
¹ Compare these examples: Invalid: ∆F '{"abc`"def"}' Valid: ∆F '{"abc""def"}' |
- ⍹ is a synonym for `⍵. It is Unicode character
⎕UCS 9081
. Either expression is valid only in Code fields and outside Quoted strings. - `⍵ or ⍹ uses an "omega index counter" (OIC) which we'll represent as Ω, common across all Code fields, which is initially set to zero,
Ω←0
. (Ω is just used for explication; don't actually use this symbol) - All Omega shortcut expressions in the f-string are evaluated left to right and are ⎕IO-independent.
- `⍵𝑑𝑑 or ⍹𝑑𝑑 sets the OIC to 𝑑𝑑,
Ω←𝑑𝑑
, and returns the expression(⍵⊃⍨Ω+⎕IO)
. Here 𝑑𝑑 must be a non-negative integer with at least 1 digit. - Bare `⍵ or ⍹ (i.e. with no digits appended) increments the OIC,
Ω+←1
, before using it as the index in the expression(⍵⊃⍨Ω+⎕IO)
. - The f-string itself (the 0-th element of ⍵) is always accessed as
`⍵0
or⍹0
. The omega with implicit index always increments its index before use, i.e. starting by default with`⍵1
or⍹1
. - If an element of the dfn's right argument ⍵ is accessed at runtime via any means, shortcut or traditional, that element must exist.
- Syntax:
[⍺←''''] `W ⍵
. - Let
L←0⊃2⍴⍺
andR←1⊃2⍴⍺
. - Wrap each row
X′
of the simple arraysX
in⍵
(or the entire arrayX
if a simple vector or scalar) in decoratorsL
andR
:L,(⍕X′),R
. ⍵
is an array of any shape and depth.L
andR
are char. vectors or scalars or⍬
(treated as''
).- If there is one scalar or enclosed vector
⍺
, it is replicated per (2) above. - By default,
⍺← ''''
,i.e. APL quotes will wrap the array ⍵, row by row, whether character, numeric or otherwise.
Show/Hide Appendices
- If
options[0]
is¯1
, then ∆F returns a character vector that contains the source code for the dfn that would have been returned via the DFN option,options[0]=1
. If DBG is also set, newlines from`◇
are shown as visible
. However, since this option returns the code string verbatim, the DBG option won't display the code string redundantly.