Python Equivalents for APL Functions

Table of Contents

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))

Author: Jason Walsh

j@wal.sh

Last Updated: 2024-08-14 06:08:49