Skip to content

Commit 3542161

Browse files
committed
A couple of tweaks to string functions, plus documentation improvements
1 parent 4ee83ea commit 3542161

File tree

3 files changed

+90
-34
lines changed

3 files changed

+90
-34
lines changed

docs/builtins.md

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ streams of pseudo-random numbers.
240240
: A *Beta(2,2)* distribution pseudo-random source.
241241

242242
`normal(` [ *seed* ] `)`
243-
: A *Normal(0,1)* distribution pseudo-random source.
243+
: A *Normal(0,1)* distribution pseudo-random source. The output range is
244+
notionally unbounded, but in practice limited by the resolution of the
245+
algorithm to $\pm 9.4193$.
244246

245247
`uniform(` [ *seed* ] `)`
246248
: A *Uniform(0,1)* distribution pseudo-random source.
@@ -255,12 +257,15 @@ create a new stream of pseudo-random numbers for each beat of the main clock.
255257
Multiplying or dividing this seed value then allows for different intervals,
256258
e.g., four times a beat: `uniform(:foo;beat*4)`.
257259

258-
The pseudo-random streams can be indexed with a range vector to generate a
259-
vector of numbers, e.g.: `uniform(:some_seed)[..100]` will generate a 100-vector
260-
of uniformly distributed numbers. The streams are arbitrarily long and are
261-
unit-cost to call, so indexing the billionth number takes the same amount of
262-
time as the 0th. Negative indices wrap around to the end of the 64-bit unsigned
263-
integer index range.
260+
Pseudo-random streams can be indexed with a range vector to generate a vector
261+
of numbers, e.g.: `uniform(:some_seed)[..100]` will generate a 100-vector of
262+
uniformly distributed numbers. Streams are *very* long (see note below) and are
263+
unit-cost to index – so indexing the billionth number takes the same amount of
264+
time as the first. Negative indices wrap around to the end of the stream.
265+
266+
Streams are considered to be `true` in conditional expressions (`if`, `and`,
267+
etc.). In all other aspects, they behave like the `null` vector, e.g., attempts
268+
to use them in mathematical expressions will evaluate to `null`.
264269

265270
For example:
266271

@@ -294,9 +299,40 @@ pick off the first few values, e.g.:
294299
let x;y;z = uniform(:position)
295300
```
296301

297-
Streams are considered to be `true` in conditional expressions (`if`, `and`,
298-
etc.). In all other aspects, they behave like the `null` vector, e.g., attempts
299-
to use them in mathematical expressions will evaluate to `null`.
302+
There is little difference in pseudo-randomness between selecting a new seed
303+
and selecting a different index. The example above could be written
304+
equivalently as:
305+
306+
```flitter
307+
let SIZE=1280;720
308+
309+
!window size=SIZE
310+
!canvas
311+
for i in ..10
312+
let x;y;h=uniform(:x;i;beat)
313+
r=2*beta(:r;beat)[i]
314+
!path
315+
!ellipse point=(x;y)*SIZE radius=r*50
316+
!fill color=hsv(h;1;1)
317+
```
318+
319+
This creates a new uniform distribution for each circle and beat, and then pulls
320+
three values from it. Note that `r` is still set by indexing a single stream
321+
per beat, as a single name let will bind the whole sequence rather than an
322+
item from it. There is a slight performance benefit in reading large numbers
323+
of values from a single sequence over reading a small number of values from a
324+
large number of sequences.
325+
326+
:::{note}
327+
The `uniform()` and `normal()` streams have an internal index range of
328+
$[0, 2^{64})$ and `beta()` streams have an internal index range of
329+
$[0, 2^{62})$. However, as all numbers in **Flitter** are double-precision
330+
floating points, the actual index range is in practice limited by the safe
331+
integer range of double-precision: $(-2^{53}, 2^{53})$. As the internal range
332+
is significantly larger than this, the negative portion of the safe range
333+
(which maps to the end of the internal range) does not overlap with the
334+
positive portion.
335+
:::
300336

301337
## Noise functions
302338

@@ -424,17 +460,21 @@ coordinates *L*, *C* and *h*. Hue *h* is in *turns*, i.e., is in the range
424460
## Text functions
425461

426462
`chr(` *o* `)`
427-
: Return the unicode character identified by the ordinal number *o*. If *o* is
428-
a multi-element
463+
: Return the Unicode character identified by the ordinal number *o*. If *o* is
464+
a multi-element vector then the result will be the Unicode string formed by
465+
concatenating each character. The value(s) of *o* is/are rounded *down* if not
466+
integer.
429467

430468
`ord(` *c* `)`
431-
: Return the ordinal number of unicode character *c* (interpreted as a string).
469+
: Return the ordinal number of Unicode character *c* (interpreted as a string).
432470
If *c* evaluates to a multi-character string then a vector of the ordinal number
433471
of each character will be returned.
434472

435-
`split(` *text* `)`
436-
: Return a vector formed by splitting the string *text* at newlines (not
437-
included).
473+
`split(` *text* `,` [ `separator="\n"` ] `)`
474+
: Return a vector formed by splitting the string *text* at each instance of
475+
*separator*, which may be a multiple character Unicode string and which is not
476+
included in the result strings. Empty values are stripped from the end (i.e.,
477+
trailing sequences of *separator* are ignored).
438478

439479
`ord()` and `chr()` can be used together with indexing to isolate a specific
440480
character range from a string. For example:
@@ -454,35 +494,42 @@ shell "glob" pattern. Files are matched relative to the directory containing
454494
the running program.
455495

456496
`csv(` *filename* `,` *row* `)`
457-
: Return a vector of values obtained by reading a
458-
specific *row* (indexed from *0*) from the CSV file with the given *filename*;
459-
this function intelligently caches and will convert numeric-looking columns in
460-
the row into numeric values.
497+
: Return a vector of values obtained by reading a specific *row* (indexed from
498+
*0*) from the Unicode CSV file with the given *filename*; numeric-looking
499+
columns in the row will be converted into numeric values, anything else will be
500+
a Unicode string.
461501

462502
`read(` *filename* `)`
463-
: Returns a single string value containing the entire text of *filename* (this
464-
function intelligently caches).
503+
: Returns a single string value containing the entire text of *filename*.
465504

466505
`read_bytes(` *filename* `)`
467-
: Returns a numeric vector value containing the entire text of *filename* (this
468-
function intelligently caches) as a sequence of bytes. This is a *very*
469-
memory-inefficient function as each byte will be represented by a
470-
double-precision floating point value.
506+
: Returns a numeric vector value containing the entire byte contents of
507+
*filename* as a vector of numbers in the range $[0,255]$. This is a *very*
508+
memory-inefficient function as each byte will be inflated to a double-precision
509+
floating point value (8 bytes).
510+
511+
:::{note}
512+
The `csv`, `read` and `read_bytes` functions intelligently cache the results of
513+
the read for performance, but will respond immediately to the file changing (as
514+
detected by a change to the file modification time).
515+
:::
471516

472517
## Texture sampling
473518

474519
The `sample` function allows reading colors from the *previous* rendered image
475520
of a referenced window node. See the explanation of the
476521
[`!reference`](windows.md#reference) node for more information on references.
477522

478-
`sample(` *id* `,` *coordinate(s)* [ `,` *default* ] `)`
523+
`sample(` *id* `,` *coordinates* [ `,` *default* ] `)`
479524
: Given the `id` of a [window node](windows.md) and a *2n*-vector of X/Y
480525
coordinates in the *[0,1)* range, return a *4n*-vector of RGBA color values.
481526
These color values *may* be outside of the normal *[0,1)* range if the
482527
`colorbits` depth of the node is greater than 8. If (any of) the coordinates
483528
are outside of the *[0,1)* range then the returned color will either be
484529
`0;0;0;0` or `default` if specified (and a 4-vector).
485530

486-
Note that use of this function *may* cause a significant slow-down in the
487-
execution of the engine as rendering will need to be completed before the
488-
texture can be read, causing a GPU pipeline stall.
531+
:::{warning}
532+
Use of the `sample` function can cause a significant slow-down in the execution
533+
of the engine, as reading from a texture stalls execution until the GPU has
534+
completed rendering the previous pipeline.
535+
:::

src/flitter/language/functions.pyx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,21 @@ def chrv(Vector ordinals):
211211
cdef str text = ""
212212
cdef int64_t i
213213
for i in range(ordinals.length):
214-
text += chr(<int>ordinals.numbers[i])
214+
text += chr(<int>floor(ordinals.numbers[i]))
215215
cdef Vector result = Vector.__new__(Vector)
216216
result.objects = (text,)
217217
result.length = 1
218218
return result
219219

220220

221-
def split(Vector text):
222-
if text.length == 0:
221+
def split(Vector text, Vector separator=Vector('\n')):
222+
if text.length == 0 or separator.length == 0:
223223
return null_
224-
return Vector._coerce(text.as_string().rstrip('\n').split('\n'))
224+
cdef str sep = separator.as_string()
225+
cdef list values = text.as_string().split(sep)
226+
while values[-1] == '':
227+
values.pop()
228+
return Vector._coerce(values)
225229

226230

227231
def lenv(Vector xs not None):

tests/test_functions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ def test_chr(self):
669669
self.assertEqual(chrv(Vector('A')), null)
670670
self.assertEqual(chrv(Vector([65])), Vector(['A']))
671671
self.assertEqual(chrv(Vector([65, 66])), Vector(['AB']))
672+
self.assertEqual(chrv(Vector.range(65, 67, 0.25)), Vector(['AAAABBBB']))
672673

673674
def test_split(self):
674675
self.assertEqual(split(null), null)
@@ -677,6 +678,10 @@ def test_split(self):
677678
self.assertEqual(split(Vector(['Hello\nworld!'])), Vector(['Hello', 'world!']))
678679
self.assertEqual(split(Vector(['Hello\nworld!\n'])), Vector(['Hello', 'world!']))
679680
self.assertEqual(split(Vector(['Hello\n\nworld!\n'])), Vector(['Hello', '', 'world!']))
681+
self.assertEqual(split(Vector(['Hello world!']), Vector(' ')), Vector(['Hello', 'world!']))
682+
self.assertEqual(split(Vector(['Hello world!\n']), Vector(' ')), Vector(['Hello', 'world!\n']))
683+
self.assertEqual(split(Vector(['Hello world!\n']), Vector('o')), Vector(['Hell', ' w', 'rld!\n']))
684+
self.assertEqual(split(Vector(['Hello world! oo ']), Vector('o ')), Vector(['Hell', 'world! o']))
680685

681686

682687
class TestColorFuncs(utils.TestCase):

0 commit comments

Comments
 (0)