An OADL intrinsic procedure is one which is built-in to the OADL
machine. It is not possible to place the address of an intrinsic
procedure in a variable. Additionally, the number of arguments to
intrinsic procedures is generally checked at compile-time. All OADL
intrinsic procedures except for typeof
are found
in the oadl
namespace.
These intrinsics deal with procedure arguments:
arg(
n)
nargs()
argvec()
List
containing all of the
arguments to the current procedure.These intrinsics modify the execution of the OADL machine:
halt()
setjmp(
arr)
longjmp()
non-local jumplongjmp(
arr,
val)
setjmp()
was calledsave(
filename)
restore(
filename)
restart()
These intrinsics manage various aspects of the dynamic memory subsystem:
gc()
protect(
val)
readonly(
val)
readonly(
obj,
pub)
deleted(
obj)
true
if the given obj has
been marked for deletionperm(
val)
transient(
val)
These intrinsics return information about the types of their arguments:
typeof(
val)
Int
, Null
,
Float
, etc. Unlike all other intrinsic
procedures, typeof()
is found in the global
namespace.typecheck(
typ,
val)
TypeCheck
exception is
thrown. Returns true
if the type matches.is_a(
obj,cls)
true
if the class cls is a
parent of the given object obj, and
false
otherwise. Same as the
?=
comparison operator.These procedures are all found in the global namespace. See the chapter on Input / Output for more information about them.
getchar()
getchar(
file)
print(
fmtstring,
arg, ...)
print(
file,
fmtstring, arg, ...)
putchar(
ch)
putchar(
file,
ch)
ungetc(
ch)
ungetc(
file,
ch)
read(
fmtstring,
typ, ...)
read(
file,
fmtstring, typ, ...)
read(
string,
fmtstring, typ, ...)
readstr()
readstr(
file)
say(
arg,
arg, ...)
say(
file,
arg, arg, ...)
There are a couple of other miscellaneous intrinsic procedures supported by OADL:
matchvec()
List
of all of the match
pattern substrings ?1
, ?2
,
etc.format
(
fmtstring,
arg, ...)
Several methods are predefined for all OADL classes. They may be overridden by the OADL programmer. See the chapter on Classes for a detailed description of these methods:
.create()
Object
is created.destroy()
.operator
{}(
args)
Intrinsic methods are provided by OADL to perform a broad range of
operations. Note that, unlike programmer-defined methods, OADL
intrinsic methods can operate on values with types other than
Object
.
.arrbase()
Char
, Int
, etc.) or
Array
if arr is a heterogeneous
List
or Array
.
.isarray()
true
if val is an array of
any kind (List
, String
,
PackInt
, etc.) and false
otherwise..ischar()
true
if val is a scalar
character of any kind (Char
or
WideChar
) and false
otherwise..isfloat()
true
if val is a floating
point scalar of any kind (Half
,
Float
, or Double
) and
false
otherwise..isinteger()
true
if val is an integral
scalar of any kind (Byte
,
Ubyte
, Short
,
Ushort
, Int
,
Uint
, Long
, or
Ulong
) and false
otherwise..isnumeric()
true
if val is a numeric
scalar of any kind (Byte
,
Ubyte
, Short
,
Ushort
, Int
,
Uint
, Long
,
Ulong
, Half
,
Float
, or Double
) and
false
otherwise..isstring()
true
if val is string of
any kind (String
or
WideString
) and false
otherwise..length()
public
properties of an object, or the number of currently defined
key/value pairs in a dictionary..maxval()
Float
, Int
, etc.).minval()
Float
, Int
, etc.).packtype()
Int.packtype()
returns PackInt
).promote(val1
)
Type
they are used directly. If they are
not an OADL TYpe
, their base types are used as
the indexes into the table..rank()
rank
of a
scalar is 0..shape()
PackInt
array which is the
multi-dimensional shape of val. The
shape
of a scalar is the empty packed integer
array 0->iterate().sizeof()
sizeof
a scalar is 1..copy()
@
operator. The new copy is
neither permanent nor read-only regardless of whether val is
permanent or read-only..deepcopy()
@@
operator. The new copy is
neither permanent nor read-only regardless of whether val is
permanent or read-only..readonly()
.readonly(
pub)
public
property pub of
Object
obj is read-only. A
TypeCheck
exception is thrown if val,
obj, or pub is not of an appropriate type..transient()
true
if the given val is
located in dynamically maintained memory subject to garbage
collection, and false
otherwise. A
TypeCheck
exception is thrown if val is
not of an appropriate type.See the chapter on Input / Output for more information about these intrinsic methods.
.binary()
.clearerr()
.close()
.close(
disp)
.feof()
.ferror()
.fflush()
.fseek(
offs,
whence)
.ftell()
.getchar()
.getswab()
.ispipe()
.print(
fmtstr,
arg, ...)
.putchar()
.read(
typ,
typ, ...)
.read(
fmtstr,
typ, typ, ...)
.read(
fmtstr,
typ, typ, ...)
.readonly()
.readstr()
.rewind()
.say(
arg,
arg, ...)
.setswab(
bSwab)
.ungetc()
.write(
val,
val, ...)
.abs()
.clamp(
lower,
upper)
.fix2flt(
typ)
.flt2fix(
typ)
fix2flt()
. Convert a value
or array of values from floating point to a specified 8- or 16-bit
signed or unsigned fixed-point number..lerp(
lower,
upper)
.max(
val1,
val2, ...)
.min(
val1,
val2, ...)
.satadd(
val1,
lower, upper)
.satsub(
val1,
lower, upper)
.signum()
Many of these methods were inspired by the programming language APL, which is an innovative interactive language developed in the 1960s to perform many array calculations and general-purpose computing tasks. There are a few conventions that should be noted:
List
, String
,
PackInt
, etc.).operator
op or `
op syntax (for operators)
or with the public::
pub syntax (for
methods). Procedures can be passed as usual..rank() +
axis, so axis
number -1 is the last axis, axis number -2 is the next-to-last
axis, and so on. Typically, if no axis is specified, the
last axis of an array is used.Unlike APL, English names of the various array operations are used for the array intrinsic methods.
.accum(
op)
.accum(
op,
axis)
\
arr and op
\[
axis]
arr..arrcmp(
arr1)
.concat(
b,
c,
...)
##
operator..decode(
val1)
⊥
val1.disclose()
⊃
val.drop(
num)
.drop(
num,
axis)
↓
arr
and num
↓[
axis]
arr.enclose()
.enclose(
axis)
⊂
val or
⊂[
axis]
val..encode(
val1)
⊤
val1.flatten()
.increment(
shp)
nil
if the
next multi-dimensional index would step past the array bounds. This
is useful with the "flattened" indexing operator,
especially in combination with the
arr.stride()
intrinsic method:arr = "abcdef".reshape(2,3) arr abc def shp = arr.shape() idx = (0).reshape(arr.rank()) // Start with [0,0] strd = arr.stride() // Needed to compute offset do { var n = idx.inner(`+,`*,strd); // Sum of products of idx and stride "arr[", idx, "] == ", arr#[n], '\n'; // Note: flattened index operator idx = idx.increment(shp); // Increment to next index in shape } while (idx != nil) arr[0 0] == a arr[0 1] == b arr[0 2] == c arr[1 0] == d arr[1 1] == e arr[1 2] == f
.inner(
op0,
op1, arr1)
.
op1 arr1. The
inner product is probably the most difficult operation to fully
explain. In the simple case of two vectors, it is equivalent to:(
vec0 op1
vec1).reduce(
op0)
/¨(⊂[⍴⍴
arr0]
arr0)∘.
op1⊂[1]
arr1// Enclose arr0 along the last axis var encl0 = arr0.enclose(-1); // Enclose arr1 along the first axis var encl1 = arr1.enclose(0); // Compute outer product of enclosed arrays with second operator var out = encl0.outer(op1,encl1); // For each element in the outer product, reduce it by the first operator var result = new Array(out.shape()); forall (out#[i]) result#[i] = out#[i].disclose().reduce(op0); // Pack the result result = result.pack(); // Convert single-element vector to scalar if (result.sizeof() == 1) result = result#[0];
.intersect(
arr)
∩
arr.iterate()
PackInt
array with a shape of val, initialized to the numbers from 0
to n-1 inclusive, where n is the product of all of
the elements of val. Note that val may also be a
scalar; in this case, care must be taken to prevent OADL from
interpreting the dot operator as part of a floating point constant.
The normal convention is to place parentheses around the scalar in
this case; e.g. (3).iterate()
..laminate(
arr1)
.laminate(
arr1,
axis)
,
arr1 and arr0
,[
axis]
arr1.member(
arr)
∈
arr.nreduce(
n,
op)
.nreduce(
n,
op, axis)
/
arr
and n op
/[
axis]
arr.
The nreduce()
method inserts the given operator
op between each element of each n-long subset of the
array, along the specified axis. Note that the result is evaluated
right-to-left. This may be changed to right-to-left order by
specifying a negative n. The resulting array has the same
rank as the original array, but its size along the specified axis
will be reduced by n-1.a = {"a", "b", "c", "d", "e", "f", "g"} a.nreduce(3, `##) abc bcd cde def efg arr.nreduce(-3,`##) cba dcb edc fed gef
.outer(
op,
arr1)
∘.
op arr1. The outer
product takes corresponding elements from arr0
and arr1 and applies op to them. For
vectors, this produces an operator table:x = [2,3,4] y = [1,2,3,4] x.outer(`*, y) 2 4 6 8 3 6 9 12 4 8 12 16
More generally, the destination array has a shape which is the
concatenation of the shapes of arr0 and
arr1. Each element of the destination array is
assigned thus:
result[
i00,
i01, ..., i10, i11,
...] =
arr0[
i00,
i01, ...]
op
arr1[
i10,
i11, ...]
.pack()
.position(
val)
⍳
val.ravel()
.ravel(
axis)
,
arr and
,[
axis]
arr.reduce(
op)
.reduce(
op,
axis)
/
arr and
op /[
axis]
arr. The reduce()
method inserts the
given operator op between each element of the array, along
the specified axis. Note that the result is evaluated
right-to-left. This is for consistency with APL. For example:arr=(10).iterate() arr.reduce(`-) -5 // Left-to-right reduce evaluation 0-1-2-3-4-5-6-7-8-9 -45 // Right-to-left reduce evaluation - per APL and OADL 0-(1-(2-(3-(4-(5-(6-(7-(8-9)))))))) -5
.replicate(
key)
.replicate(
key,
axis)
/
arr
and key
/[
axis]
arr
and also similar to APL key \
arr
and key.reshape(
d0,
d1, ...)
.reshape(
shp)
[
d0, d1,
...]
. Note that the single-argument form
can specificy a single-dimensional PackInt
which will be the shape of the new array. Dictionaries may not be
made smaller, only larger. Returns the new resized copy of
val..reverse()
.reverse(
axis)
⌽
arr and
⌽[
axis]
arr. Reverses the order of elements along the given axis.
For example:a = [2,3].iterate() a.reverse() 2 1 0 5 4 3 a.reverse(0) 3 4 5 0 1 2
.rotate(
num)
.rotate(
num,
axis)
⊖
arr
and num
⌽[
axis]
arr. Rotates the elements of an array along the given axis
by the given number of places. For example:arr = [4,4].iterate() // Rotate arr by 1 place along last axis arr.rotate(1) 3 0 1 2 7 4 5 6 11 8 9 10 15 12 13 14 // Rotate arr by 2 places along last axis arr.rotate(2) 2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13 // Rotate arr by 1 place along first axis arr.rotate(1,0) 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11
.sort()
.sort(
cmp)
operator <
is assumed..stride()
#[]
.subr(
beg0,
end0, beg1, end1, ...
)
nil
, then the subrange goes to the end of
that particular dimension. If arr is not an array, or if any
begn or endn is not an integer
(or nil
) then the TypeCheck
exception is thrown. The subrange is clamped to the actual bounds
of the array or string. If endn is less than
begn the result is an empty array or string. This
is closely related to the use of :
in array
indices..take(
num)
.take(
num,
axis)
↑
arr
and num
↑[
axis]
arr.transpose()
.transpose(
code)
⍉
arr and
code ⍉
arr. If no code
is given, reverses the order of axes of an array. The code
is a vector containing instructions on the order of axes. The
length of the code must be the same as the rank of
arr. Each element of the code is an axis from
arr. If each element of code is unique, it simply
specifies a new order for the result axes. If an element in
code is repeated, then axes of the array are mapped together
and the rank of the result will be less than the original array.
Additionally, the maximum axis allowed to be present in code
is reduced. If the axes that are mapped together are of different
lengths, the shorter axis controls the number of elements for the
destination array along that axis. In effect, repeated axes
traverse a diagonal of the original array For example:arr = [2,3].iterate() arr.transpose() 0 3 1 4 2 5 // 2 slabs of 3 rows by 4 columns arr = [2,3,4].iterate() arr 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // Orig. axis 0 goes to new axis 1 (old slabs -> new rows) // Orig. axis 1 goes to new axis 0 (old rows -> new slabs) // Orig. axis 2 goes to new axis 2 (old cols -> new cols; NOP) arr.transpose([1,0,2]) 0 1 2 3 12 13 14 15 4 5 6 7 16 17 18 19 8 9 10 11 20 21 22 23 // Orig. axis 0 goes to new axis 1 (old slabs -> new rows) // Orig. axis 1 goes to new axis 0 (old rows -> new slabs) // Orig. axis 2 ALSO goes to new axis 1 (old cols -> new rows ALSO) // (The shorter of axis 0 and axis 2 is axis 0. The new // columns are taken from arr[0,x,0] and arr[1,x,1]) arr.transpose([1,0,1]) 0 13 4 17 8 21
.union(
arr)
∪
arr.unique()
∪
vec.unpack()
List
(for single-dimensional arrays)
or an Array
(for multi-dimensional arrays)..width()
.without(
arr)
~
arr