Python Equivalents for APL Functions
Table of Contents
- Tutorial: APL Functions Across Programming Languages
- Reverse
- Rotate
- Ravel
- Reshape
- Transpose
- Ceiling
- Floor
- Absolute Value
- Index Generator
- Reshape with Scalar
- Reduce
- Scan
- Outer Product
- First
- Take
- Drop
- Reverse First
- Membership
- Unique / Nub
- Grade Up
- Grade Down
- TODO Encode
- TODO Decode
- TODO Interval Index
- TODO Matrix Inverse
- Shape
- Plus Reduce (Sum)
- Multiply Reduce (Product)
- Minimum Reduce
- Maximum Reduce
- Index of First Match
- Thread
- Apply Transforms
Tutorial: APL Functions Across Programming Languages
This tutorial explores essential APL functions and demonstrates their equivalent implementations in Python.
Reverse
APL
⌽ 1 2 3 4 5= → =5 4 3 2 1
Python
from typing import List, TypeVar T = TypeVar('T') def reverse(xs: List[T]) -> List[T]: return xs[::-1] print(reverse([1, 2, 3, 4, 5])) # [5, 4, 3, 2, 1]
Rotate
APL
2⌽ 1 2 3 4 5= → =3 4 5 1 2
Python
def rotate(n: int, xs: List[T]) -> List[T]: n = n % len(xs) return xs[n:] + xs[:n] print(rotate(2, [1, 2, 3, 4, 5])) # [3, 4, 5, 1, 2]
Ravel
APL
, 2 2 ⍴ 1 2 3 4= → =1 2 3 4
Python
from typing import List, Any def ravel(matrix: List[List[Any]]) -> List[Any]: return [item for row in matrix for item in row] print(ravel([[1, 2], [3, 4]])) # [1, 2, 3, 4]
Reshape
APL
3 2 ⍴ 1 2 3 4 5 6= → =(1 2)(3 4)(5 6)
Python
from typing import List, Any def reshape(shape: List[int], xs: List[Any]) -> List[List[Any]]: result = [] for i in range(shape[0]): result.append(xs[i*shape[1]:(i+1)*shape[1]]) return result print(reshape([3, 2], [1, 2, 3, 4, 5, 6])) # [[1, 2], [3, 4], [5, 6]]
Transpose
APL
⍉ 3 2 ⍴ 1 2 3 4 5 6= → =(1 3 5)(2 4 6)
Python
from typing import List, TypeVar T = TypeVar('T') def transpose(matrix: List[List[T]]) -> List[List[T]]: return list(map(list, zip(*matrix))) print(transpose([[1, 2], [3, 4], [5, 6]])) # [[1, 3, 5], [2, 4, 6]]
Ceiling
APL
⌈ 3.7 ¯1.1 0 5.1= → =4 ¯1 0 6
Python
import math from typing import List def ceiling(xs: List[float]) -> List[int]: return [math.ceil(x) for x in xs] print(ceiling([3.7, -1.1, 0, 5.1])) # [4, -1, 0, 6]
Floor
APL
⌊ 3.7 ¯1.1 0 5.1= → =3 ¯2 0 5
Python
import math from typing import List def floor(xs: List[float]) -> List[int]: return [math.floor(x) for x in xs] print(floor([3.7, -1.1, 0, 5.1])) # [3, -2, 0, 5]
Absolute Value
APL
| 1 ¯2 3 ¯4= → =1 2 3 4
Python
from typing import List def absolute(xs: List[float]) -> List[float]: return [abs(x) for x in xs] print(absolute([1, -2, 3, -4])) # [1, 2, 3, 4]
Index Generator
APL
⍳ 5= → =1 2 3 4 5
Python
from typing import List def index_gen(n: int) -> List[int]: return list(range(1, n+1)) print(index_gen(5)) # [1, 2, 3, 4, 5]
Reshape with Scalar
APL
5⍴2= → =2 2 2 2 2
Python
from typing import List, TypeVar T = TypeVar('T') def scalar_reshape(n: int, x: T) -> List[T]: return [x] * n print(scalar_reshape(5, 2)) # [2, 2, 2, 2, 2]
Reduce
APL
+/ 1 2 3 4 5= → =15
Python
import functools from typing import List, Callable, TypeVar T = TypeVar('T') def reduce(f: Callable[[T, T], T], xs: List[T]) -> T: return functools.reduce(f, xs) print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])) # 15
Scan
APL
+\ 1 2 3 4 5= → =1 3 6 10 15
Python
import itertools from typing import List, Callable, TypeVar T = TypeVar('T') def scan(f: Callable[[T, T], T], xs: List[T]) -> List[T]: return list(itertools.accumulate(xs, f)) print(scan(lambda x, y: x + y, [1, 2, 3, 4, 5])) # [1, 3, 6, 10, 15]
Outer Product
APL
1 2 3 ∘.× 10 20 30= → =(10 20 30)(20 40 60)(30 60 90)
Python
from typing import List, Callable, TypeVar T = TypeVar('T') def outer_product(f: Callable[[T, T], T], xs: List[T], ys: List[T]) -> List[List[T]]: return [[f(x, y) for y in ys] for x in xs] print(outer_product(lambda x, y: x * y, [1, 2, 3], [10, 20, 30])) # [[10, 20, 30], [20, 40, 60], [30, 60, 90]]
First
APL
⊃ 1 2 3 4 5= → =1
Python
from typing import List, TypeVar T = TypeVar('T') def first(xs: List[T]) -> T: return xs[0] print(first([1, 2, 3, 4, 5])) # 1
Take
APL
3↑1 2 3 4 5= → =1 2 3
Python
from typing import List, TypeVar T = TypeVar('T') def take(n: int, xs: List[T]) -> List[T]: return xs[:n] print(take(3, [1, 2, 3, 4, 5])) # [1, 2, 3]
Drop
APL
2↓1 2 3 4 5= → =3 4 5
Python
from typing import List, TypeVar T = TypeVar('T') def drop(n: int, xs: List[T]) -> List[T]: return xs[n:] print(drop(2, [1, 2, 3, 4, 5])) # [3, 4, 5]
Reverse First
APL
⊖ 3 3 ⍴ ⍳9= → =(7 8 9)(4 5 6)(1 2 3)
Python
from typing import List, Any def reverse_first(matrix: List[List[Any]]) -> List[List[Any]]: return matrix[::-1] print(reverse_first([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) # [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
Membership
APL
2 3 4 ∊ 1 2 3 5= → =1 1 0
Python
from typing import List, TypeVar T = TypeVar('T') def membership(xs: List[T], ys: List[T]) -> List[bool]: return [x in ys for x in xs] print(membership([2, 3, 4], [1, 2, 3, 5])) # [True, True, False]
Unique / Nub
APL
∪ 1 2 3 2 1 4 5 4= → =1 2 3 4 5
Python
from typing import List, TypeVar T = TypeVar('T') def unique(xs: List[T]) -> List[T]: return list(dict.fromkeys(xs)) print(unique([1, 2, 3, 2, 1, 4, 5, 4])) # [1, 2, 3, 4, 5]
Grade Up
APL
⍋ 3 1 4 1 5 9 2 6= → =2 4 1 7 3 5 8 6
Python
from typing import List, TypeVar T = TypeVar('T') def grade_up(xs: List[T]) -> List[int]: return sorted(range(len(xs)), key=lambda i: xs[i]) print(grade_up([3, 1, 4, 1, 5, 9, 2, 6])) # [1, 3, 6, 0, 2, 4, 7, 5]
Grade Down
APL
⍒ 3 1 4 1 5 9 2 6= → =6 8 5 3 1 7 4 2
Python
from typing import List, TypeVar T = TypeVar('T') def grade_down(xs: List[T]) -> List[int]: return sorted(range(len(xs)), key=lambda i: xs[i], reverse=True) print(grade_down([3, 1, 4, 1, 5, 9, 2, 6])) # [5, 7, 4, 2, 0, 6, 3, 1]
TODO Encode
APL
2 3 4 ⊤ 29= → =1 1 1
Python
from typing import List def encode(bases: List[int], n: int) -> List[int]: result = [] for base in reversed(bases): result.append(n % base) n //= base return list(reversed(result)) print(encode([2, 3, 4], 29)) # [1, 1, 1]
TODO Decode
APL
2 3 4 ⊥ 1 1 1= → =29
Python
import functools from typing import List def decode(bases: List[int], digits: List[int]) -> int: return sum(d * functools.reduce(lambda x, y: x*y, bases[:i], 1) for i, d in enumerate(digits)) print(decode([2, 3, 4], [1, 1, 1])) # 29
TODO Interval Index
APL
3 5 7 ⍸ 1 3 4 5 6 8= → =0 1 1 2 2 3
Python
import bisect from typing import List def interval_index(intervals: List[int], values: List[int]) -> List[int]: return [bisect.bisect_left(intervals, v) for v in values] print(interval_index([3, 5, 7], [1, 3, 4, 5, 6, 8])) # [0, 1, 1, 2, 2, 3]
TODO Matrix Inverse
APL
⌹ 2 2 ⍴ 1 2 3 4= → =(¯2 1)(1.5 ¯0.5)
Python
import numpy as np from typing import List def matrix_inverse(matrix: List[List[float]]) -> List[List[float]]: return np.linalg.inv(np.array(matrix)).tolist() print(matrix_inverse([[1, 2], [3, 4]])) # [[-2.0, 1.0], [1.5, -0.5]]
Shape
APL
⍴ 3 3 ⍴ ⍳9= → =3 3
Python
from typing import List, Any def shape(matrix: List[List[Any]]) -> List[int]: return [len(matrix), len(matrix[0])] print(shape([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) # [3, 3]
Plus Reduce (Sum)
APL
+/ 1 2 3 4 5= → =15
Python
from typing import List def plus_reduce(xs: List[int]) -> int: return sum(xs) print(plus_reduce([1, 2, 3, 4, 5])) # 15
Multiply Reduce (Product)
APL
×/ 1 2 3 4 5= → =120
Python
import math from typing import List def multiply_reduce(xs: List[int]) -> int: return math.prod(xs) print(multiply_reduce([1, 2, 3, 4, 5])) # 120
Minimum Reduce
APL
⌊/ 1 2 3 4 5= → =1
Python
from typing import List def minimum_reduce(xs: List[int]) -> int: return min(xs) print(minimum_reduce([1, 2, 3, 4, 5])) # 1
Maximum Reduce
APL
⌈/ 1 2 3 4 5= → =5
Python
from typing import List def maximum_reduce(xs: List[int]) -> int: return max(xs) print(maximum_reduce([1, 2, 3, 4, 5])) # 5
Index of First Match
APL
2 3 4 ⍳ 3= → =2
Python
from typing import List def index_of_first_match(xs: List[int], x: int) -> int: try: return xs.index(x) except ValueError: return -1 # If not found, return -1 (APL would return length + 1) print(index_of_first_match([2, 3, 4], 3)) # 1
Thread
APL
thread ← {⍺⍺ ⊃∘.,⊂⍵} ⍝ Example 1: identity thread 1 2 3 ⍝ Result: 1 2 3 ⍝ Example 2: (identity add_one square) thread 1 2 3 ⍝ Result: 1 2 1 2 3 4 1 4 9 ⍝ Where: ⍝ identity ← {⍵} ⍝ add_one ← {⍵+1} ⍝ square ← {⍵*2}
Python
from typing import List, Callable, TypeVar T = TypeVar('T') def thread(xs: List[T], funcs: List[Callable[[T], T]]) -> List[T]: return [f(x) for x in xs for f in funcs] def identity(x: T) -> T: return x def add_one(x: int) -> int: return x + 1 def square(x: int) -> int: return x * x print(thread([1, 2, 3], [identity])) # [1, 2, 3] print(thread([1, 2, 3], [identity, add_one, square])) # [1, 2, 1, 2, 3, 4, 1, 4, 9]
Apply Transforms
APL
apply_transformations ← { elements ← ⍺ transformations ← ⍵ {⊃⍵ {⍺⍺ ⍵}/ ⊂⍺}¨ elements ⊢ transformations }
Python
from typing import List, Callable, TypeVar T = TypeVar('T') def apply_transformations(l: List[T], t: List[Callable[[T], T]]) -> List[T]: result = l.copy() for transform in t: result = [transform(item) for item in result] return result def remove_non_alphabetic_chars(s: str) -> str: return ''.join(c if c.isalpha() or c.isspace() else '' for c in s) def lowercase(s: str) -> str: return s.lower() def disemvowel(s: str) -> str: return ''.join(c for c in s if c.lower() not in 'aeiou') def dedupe(s: str) -> str: result = [] for char in s: if not result or char != result[-1]: result.append(char) return ''.join(result) def add_exclamation(s: str) -> str: return s + "!" transformations = [ lowercase, remove_non_alphabetic_chars, disemvowel, dedupe, add_exclamation ] utterances = [ "Secret speech Hazelton and obviously disemvowelled", "The quick brown fox jumps over the lazy dog!", "URL: https://www.example.com" ] print(apply_transformations(utterances, transformations))