Skip to content

Commit 4c0c5d4

Browse files
committed
CHANGE: fup, view, islice are now regular functions, not macros
1 parent a851b05 commit 4c0c5d4

File tree

6 files changed

+142
-195
lines changed

6 files changed

+142
-195
lines changed

unpythonic/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from .mathseq import *
3333
from .misc import *
3434
from .seq import *
35+
from .slicing import *
3536
from .tco import *
3637

3738
# HACK: break dependency loop

unpythonic/slicing.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# -*- coding: utf-8 -*-
2+
"""Operations on sequences, with native slice syntax. Syntactic sugar, pure Python."""
3+
4+
from itertools import islice as islicef
5+
from unpythonic import SequenceView, fupdate
6+
7+
view = SequenceView
8+
view.__doc__ = """Alias for ``unpythonic.collections.SequenceView``.
9+
10+
Usage::
11+
12+
view(seq)[slicestx]
13+
"""
14+
15+
def islice(iterable):
16+
"""Use itertools.islice with slice syntax.
17+
18+
Usage::
19+
20+
islice(iterable)[idx_or_slice]
21+
22+
The slicing variant calls ``itertools.islice`` with the corresponding
23+
slicing parameters.
24+
25+
As a convenience feature: a single index is interpreted as a length-1 islice
26+
starting at that index. The slice is then immediately evaluated and the item
27+
is returned.
28+
29+
Examples::
30+
31+
from unpythonic import primes, s
32+
33+
p = primes()
34+
assert tuple(islice(p)[10:15]) == (31, 37, 41, 43, 47)
35+
36+
assert tuple(islice(primes())[10:15]) == (31, 37, 41, 43, 47)
37+
38+
p = primes()
39+
assert islice(p)[10] == 31
40+
41+
odds = islice(s(1, 2, ...))[::2]
42+
assert tuple(islice(odds)[:5]) == (1, 3, 5, 7, 9)
43+
assert tuple(islice(odds)[:5]) == (11, 13, 15, 17, 19) # five more
44+
45+
**CAUTION**: Keep in mind ``itertools.islice`` does not support negative
46+
indexing for any of ``start``, ``stop`` or ``step``, and that the slicing
47+
process consumes elements from the iterable.
48+
"""
49+
# manually curry to take indices later, but expect them in subscript syntax to support slicing
50+
class islice1:
51+
"""Subscript me to perform the slicing."""
52+
def __getitem__(self, k):
53+
if isinstance(k, tuple):
54+
raise TypeError("multidimensional indexing not supported, got {}".format(k))
55+
if isinstance(k, slice):
56+
return islicef(iterable, k.start, k.stop, k.step)
57+
return tuple(islicef(iterable, k, k + 1))[0]
58+
return islice1()
59+
60+
def fup(seq):
61+
"""Functionally update a sequence.
62+
63+
Usage::
64+
65+
fup(seq)[idx_or_slice] << values
66+
67+
For when you want to be more functional than Python allows. Example::
68+
69+
from itertools import repeat
70+
71+
lst = (1, 2, 3, 4, 5)
72+
assert fup(lst)[3] << 42 == (1, 2, 3, 42, 5)
73+
assert fup(lst)[0::2] << tuple(repeat(10, 3)) == (10, 2, 10, 4, 10)
74+
75+
Limitations:
76+
77+
- Currently only one update specification is supported in a single ``fup()``.
78+
If you need more, use ``fupdate`` directly.
79+
80+
Named after the sound a sequence makes when it is hit by a functional update.
81+
"""
82+
# two-phase manual curry, first expect a subscript, then an lshift.
83+
class fup1:
84+
"""Subscript me to specify index or slice where to fupdate."""
85+
def __getitem__(self, k):
86+
if isinstance(k, tuple):
87+
raise TypeError("multidimensional indexing not supported, got {}".format(k))
88+
class fup2:
89+
"""Left-shift me with values to perform the fupdate."""
90+
def __lshift__(self, v):
91+
return fupdate(seq, k, v)
92+
return fup2()
93+
return fup1()

unpythonic/syntax/__init__.py

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from .letsyntax import let_syntax_expr, let_syntax_block, block, expr
2525
from .nb import nb as _nb
2626
from .prefix import prefix as _prefix
27-
from .slicing import fup as _fup, view as _view, islice as _islice
2827
from .tailtools import autoreturn as _autoreturn, tco as _tco, \
2928
continuations as _continuations, call_cc
3029

@@ -790,94 +789,6 @@ def quicklambda(tree, **kw):
790789

791790
# -----------------------------------------------------------------------------
792791

793-
@macros.expr
794-
def fup(tree, **kw):
795-
"""[syntax, expr] Functionally update a sequence.
796-
797-
For when you want to be more functional than Python allows.
798-
799-
Example::
800-
801-
from itertools import repeat
802-
803-
lst = (1, 2, 3, 4, 5)
804-
assert fup[lst[3] << 42] == (1, 2, 3, 42, 5)
805-
assert fup[lst[0::2] << tuple(repeat(10, 3))] == (10, 2, 10, 4, 10)
806-
807-
The transformation is::
808-
809-
fup[seq[idx] << value] --> fupdate(seq, idx, value)
810-
fup[seq[slicestx] << iterable] --> fupdate(seq, slice(...), iterable)
811-
812-
Limitations:
813-
814-
- Currently only one update specification is supported in a single ``fup[]``.
815-
816-
Named after the sound a sequence makes when it is hit by a functional update.
817-
"""
818-
return _fup(tree)
819-
820-
@macros.expr
821-
def view(tree, **kw):
822-
"""[syntax, expr] Writable view into a sequence.
823-
824-
For when you want to be more imperative than Python allows.
825-
826-
Examples::
827-
828-
lst = [1, 2, 3, 4, 5]
829-
v = view[lst[2:4]]
830-
v[:] = [10, 20]
831-
assert lst == [1, 2, 10, 20, 5]
832-
833-
lst = [1, 2, 3, 4, 5]
834-
v = view[lst]
835-
v[2:4] = [10, 20]
836-
assert lst == [1, 2, 10, 20, 5]
837-
838-
The transformation is::
839-
840-
view[seq] --> SequenceView(seq)
841-
view[seq[slicestx]] --> SequenceView(seq, slice(...))
842-
"""
843-
return _view(tree)
844-
845-
@macros.expr
846-
def islice(tree, **kw):
847-
"""[syntax, expr] Use itertools.islice with slice syntax.
848-
849-
An ``islice[]`` slicing expression transforms into a call to
850-
``itertools.islice`` with the corresponding slicing parameters.
851-
852-
As a convenience feature: inside an ``islice[]`` expression, a single index
853-
is interpreted as a length-1 islice starting at that index. The slice is then
854-
immediately evaluated and the item is returned.
855-
856-
Examples::
857-
858-
from unpythonic.syntax import macros, islice
859-
from unpythonic import primes, s
860-
861-
p = primes()
862-
assert tuple(islice[p[10:15]]) == (31, 37, 41, 43, 47)
863-
864-
assert tuple(islice[primes()[10:15]]) == (31, 37, 41, 43, 47)
865-
866-
p = primes()
867-
assert islice[p[10]] == 31
868-
869-
odds = islice[s(1, 2, ...)[::2]]
870-
assert tuple(islice[odds[:5]]) == (1, 3, 5, 7, 9)
871-
assert tuple(islice[odds[:5]]) == (11, 13, 15, 17, 19) # five more
872-
873-
**CAUTION**: Keep in mind ``itertools.islice`` does not support negative
874-
indexing for any of ``start``, ``stop`` or ``step``, and that the slicing
875-
process consumes elements from the iterable.
876-
"""
877-
return _islice(tree)
878-
879-
# -----------------------------------------------------------------------------
880-
881792
@macros.block
882793
def autoreturn(tree, **kw):
883794
"""[syntax, block] Implicit "return" in tail position, like in Lisps.

unpythonic/syntax/slicing.py

Lines changed: 0 additions & 61 deletions
This file was deleted.

unpythonic/syntax/test/test_slicing.py

Lines changed: 0 additions & 45 deletions
This file was deleted.

unpythonic/test/test_slicing.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- coding: utf-8 -*-
2+
"""Operations on sequences, with native slice syntax. Syntactic sugar, pure Python."""
3+
4+
from itertools import repeat
5+
6+
from ..slicing import fup, view, islice
7+
from ..mathseq import primes, s
8+
9+
def test():
10+
# functional update for sequences
11+
# (when you want to be more functional than Python allows)
12+
lst = (1, 2, 3, 4, 5)
13+
assert fup(lst)[3] << 42 == (1, 2, 3, 42, 5)
14+
assert fup(lst)[0::2] << tuple(repeat(10, 3)) == (10, 2, 10, 4, 10)
15+
assert fup(lst)[1::2] << tuple(repeat(10, 3)) == (1, 10, 3, 10, 5)
16+
assert fup(lst)[::2] << tuple(repeat(10, 3)) == (10, 2, 10, 4, 10)
17+
assert fup(lst)[::-1] << tuple(range(5)) == (4, 3, 2, 1, 0)
18+
assert lst == (1, 2, 3, 4, 5)
19+
20+
# writable view for sequences
21+
# (when you want to be more imperative than Python allows)
22+
lst = [1, 2, 3, 4, 5]
23+
v = view(lst)[2:4]
24+
v[:] = [10, 20]
25+
assert lst == [1, 2, 10, 20, 5]
26+
27+
lst = [1, 2, 3, 4, 5]
28+
v = view(lst)
29+
v[2:4] = [10, 20]
30+
assert lst == [1, 2, 10, 20, 5]
31+
32+
# slice syntax wrapper for itertools.islice
33+
p = primes()
34+
assert tuple(islice(p)[10:15]) == (31, 37, 41, 43, 47)
35+
36+
assert tuple(islice(primes())[10:15]) == (31, 37, 41, 43, 47)
37+
38+
p = primes()
39+
assert islice(p)[10] == 31
40+
41+
odds = islice(s(1, 2, ...))[::2]
42+
assert tuple(islice(odds)[:5]) == (1, 3, 5, 7, 9)
43+
assert tuple(islice(odds)[:5]) == (11, 13, 15, 17, 19) # five more
44+
45+
print("All tests PASSED")
46+
47+
if __name__ == '__main__':
48+
test()

0 commit comments

Comments
 (0)