Source code for abjad.sequence

import builtins
import collections
import copy
import fractions
import itertools
import math
import sys
import typing

from . import cyclictuple as _cyclictuple
from . import enums as _enums
from . import math as _math


def _partition_sequence_cyclically_by_weights_at_least(
    sequence, weights, overhang=False
):
    l_copy = list(sequence)
    result = []
    current_part = []
    target_weight_index = 0
    len_weights = len(weights)
    while l_copy:
        target_weight = weights[target_weight_index % len_weights]
        item = l_copy.pop(0)
        current_part.append(item)
        if target_weight <= _math.weight(current_part):
            result.append(current_part)
            current_part = []
            target_weight_index += 1
    assert not l_copy
    if current_part:
        if overhang:
            result.append(current_part)
    result = [type(sequence)(_) for _ in result]
    return result


def _partition_sequence_cyclically_by_weights_at_most(
    sequence, weights, overhang=False
):
    result = []
    current_part = []
    current_target_weight_index = 0
    current_target_weight = weights[current_target_weight_index]
    l_copy = list(sequence)
    while l_copy:
        current_target_weight = weights[current_target_weight_index % len(weights)]
        item = l_copy.pop(0)
        current_part_weight = _math.weight(current_part)
        candidate_part_weight = current_part_weight + _math.weight([item])
        if candidate_part_weight < current_target_weight:
            current_part.append(item)
        elif candidate_part_weight == current_target_weight:
            current_part.append(item)
            result.append(current_part)
            current_part = []
            current_target_weight_index += 1
        elif current_target_weight < candidate_part_weight:
            if current_part:
                l_copy.insert(0, item)
                result.append(current_part)
                current_part = []
                current_target_weight_index += 1
            else:
                raise Exception("elements in sequence too big.")
        else:
            raise ValueError("candidate and target rates must compare.")
    if current_part:
        if overhang:
            result.append(current_part)
    result = [type(sequence)(_) for _ in result]
    return result


def _partition_sequence_once_by_weights_at_least(sequence, weights, overhang=False):
    result = []
    current_part = []
    l_copy = list(sequence)
    for num_weight, target_weight in enumerate(weights):
        while True:
            try:
                item = l_copy.pop(0)
            except IndexError:
                if num_weight + 1 == len(weights):
                    if current_part:
                        result.append(current_part)
                        break
                raise Exception("too few elements in sequence.")
            current_part.append(item)
            if target_weight <= _math.weight(current_part):
                result.append(current_part)
                current_part = []
                break
    if l_copy:
        if overhang:
            result.append(l_copy)
    result = [type(sequence)(_) for _ in result]
    return result


def _partition_sequence_once_by_weights_at_most(sequence, weights, overhang=False):
    l_copy = list(sequence)
    result = []
    current_part = []
    for target_weight in weights:
        while True:
            try:
                item = l_copy.pop(0)
            except IndexError:
                raise Exception("too few elements in sequence.")
            current_weight = _math.weight(current_part)
            candidate_weight = current_weight + _math.weight([item])
            if candidate_weight < target_weight:
                current_part.append(item)
            elif candidate_weight == target_weight:
                current_part.append(item)
                result.append(current_part)
                current_part = []
                break
            elif target_weight < candidate_weight:
                if current_part:
                    result.append(current_part)
                    current_part = []
                    l_copy.insert(0, item)
                    break
                else:
                    raise Exception("elements in sequence too big.")
            else:
                raise ValueError("candidate and target weights must compare.")
    if overhang:
        left_over = current_part + l_copy
        if left_over:
            result.append(left_over)
    result = [type(sequence)(_) for _ in result]
    return result


[docs]def partition_by_counts( sequence, counts, *, cyclic: bool = False, enchain: bool = False, overhang=False, reversed_: bool = False, ) -> list: r""" Partitions ``sequence`` by ``counts``. Partitions sequence once by counts without overhang: .. container:: example >>> sequence = list(range(16)) >>> sequence = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=False, ... overhang=False, ... ) >>> sequence [[0, 1, 2]] >>> for part in sequence: ... part [0, 1, 2] .. container:: example Partitions sequence once by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=False, ... overhang=False, ... ) >>> for part in parts: ... part [0, 1, 2, 3] [4, 5, 6] .. container:: example Partitions sequence cyclically by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=True, ... overhang=False, ... ) >>> for part in parts: ... part [0, 1, 2] [3, 4, 5] [6, 7, 8] [9, 10, 11] [12, 13, 14] .. container:: example Partitions sequence cyclically by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=True, ... overhang=False, ... ) >>> for part in parts: ... part [0, 1, 2, 3] [4, 5, 6] [7, 8, 9, 10] [11, 12, 13] .. container:: example Partitions sequence once by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=False, ... overhang=True, ... ) >>> for part in parts: ... part [0, 1, 2] [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] .. container:: example Partitions sequence once by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=False, ... overhang=True, ... ) >>> for part in parts: ... part [0, 1, 2, 3] [4, 5, 6] [7, 8, 9, 10, 11, 12, 13, 14, 15] .. container:: example Partitions sequence cyclically by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=True, ... overhang=True, ... ) >>> for part in parts: ... part [0, 1, 2] [3, 4, 5] [6, 7, 8] [9, 10, 11] [12, 13, 14] [15] .. container:: example Partitions sequence cyclically by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=True, ... overhang=True, ... ) >>> for part in parts: ... part [0, 1, 2, 3] [4, 5, 6] [7, 8, 9, 10] [11, 12, 13] [14, 15] .. container:: example Reverse-partitions sequence once by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=False, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part [13, 14, 15] .. container:: example Reverse-partitions sequence once by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=False, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part [9, 10, 11] [12, 13, 14, 15] .. container:: example Reverse-partitions sequence cyclically by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=True, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part [1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] [13, 14, 15] .. container:: example Reverse-partitions sequence cyclically by counts without overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=True, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part [2, 3, 4] [5, 6, 7, 8] [9, 10, 11] [12, 13, 14, 15] .. container:: example Reverse-partitions sequence once by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=False, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] [13, 14, 15] .. container:: example Reverse-partitions sequence once by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=False, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part [0, 1, 2, 3, 4, 5, 6, 7, 8] [9, 10, 11] [12, 13, 14, 15] .. container:: example Reverse-partitions sequence cyclically by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=True, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part [0] [1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] [13, 14, 15] .. container:: example Reverse-partitions sequence cyclically by counts with overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [4, 3], ... cyclic=True, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part [0, 1] [2, 3, 4] [5, 6, 7, 8] [9, 10, 11] [12, 13, 14, 15] .. container:: example Partitions sequence once by counts and asserts that sequence partitions exactly (with no overhang): >>> sequence = list(range(10)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [2, 3, 5], ... cyclic=False, ... overhang=abjad.EXACT, ... ) >>> for part in parts: ... part [0, 1] [2, 3, 4] [5, 6, 7, 8, 9] .. container:: example Partitions sequence cyclically by counts and asserts that sequence partitions exactly. Exact partitioning means partitioning with no overhang: >>> sequence = list(range(10)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [2], ... cyclic=True, ... overhang=abjad.EXACT, ... ) >>> for part in parts: ... part [0, 1] [2, 3] [4, 5] [6, 7] [8, 9] .. container:: example Partitions string: >>> sequence = list("some text") >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [3], ... cyclic=False, ... overhang=True, ... ) >>> for part in parts: ... part ['s', 'o', 'm'] ['e', ' ', 't', 'e', 'x', 't'] .. container:: example Partitions sequence cyclically into enchained parts by counts; truncates overhang: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [2, 6], ... cyclic=True, ... enchain=True, ... overhang=False, ... ) >>> for part in parts: ... part [0, 1] [1, 2, 3, 4, 5, 6] [6, 7] [7, 8, 9, 10, 11, 12] [12, 13] .. container:: example Partitions sequence cyclically into enchained parts by counts; returns overhang at end: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [2, 6], ... cyclic=True, ... enchain=True, ... overhang=True, ... ) >>> for part in parts: ... part [0, 1] [1, 2, 3, 4, 5, 6] [6, 7] [7, 8, 9, 10, 11, 12] [12, 13] [13, 14, 15] .. container:: example REGRESSION: partitions sequence cyclically into enchained parts by counts; does not return false 1-element part at end: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts( ... sequence, ... [5], ... cyclic=True, ... enchain=True, ... overhang=True, ... ) >>> for part in parts: ... part [0, 1, 2, 3, 4] [4, 5, 6, 7, 8] [8, 9, 10, 11, 12] [12, 13, 14, 15] .. container:: example Edge case: empty counts nests sequence and ignores keywords: >>> sequence = list(range(16)) >>> parts = abjad.sequence.partition_by_counts(sequence, []) >>> for part in parts: ... part [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] Returns list of ``sequence`` types. """ if not all(isinstance(_, int) and 0 <= _ for _ in counts): raise Exception(f"must be nonnegative integers: {counts!r}.") if reversed_: sequence = type(sequence)(reversed(sequence)) if counts: counts = _cyclictuple.CyclicTuple(counts) else: return type(sequence)([sequence]) result = [] i, start = 0, 0 while True: count = counts[i] stop = start + count part = sequence[start:stop] if len(sequence) < stop: if enchain and len(part) == 1: part = None break result.append(part) start = stop i += 1 if not cyclic and len(counts) <= i: part = sequence[start:] break if enchain: start -= 1 if part: if overhang is True: result.append(part) elif overhang is _enums.EXACT and len(part) == count: result.append(part) elif overhang is _enums.EXACT and len(part) != count: raise Exception("sequence does not partition exactly.") if reversed_: result_ = [] for part in reversed(result): part_type = type(part) part = reversed(part) part = part_type(part) result_.append(part) result = result_ return result
[docs]def partition_by_ratio_of_lengths(sequence, ratio: tuple[int, ...]) -> list: r""" Partitions ``sequence`` by ``ratio`` of lengths. Partitions sequence by ``1:1:1`` ratio: .. container:: example >>> numbers = list(range(10)) >>> ratio = (1, 1, 1) >>> for part in abjad.sequence.partition_by_ratio_of_lengths(numbers, ratio): ... part [0, 1, 2] [3, 4, 5, 6] [7, 8, 9] .. container:: example Partitions sequence by ``1:1:2`` ratio: >>> numbers = list(range(10)) >>> ratio = (1, 1, 2) >>> for part in abjad.sequence.partition_by_ratio_of_lengths(numbers, ratio): ... part [0, 1, 2] [3, 4] [5, 6, 7, 8, 9] Returns list of ``sequence`` types. """ assert isinstance(ratio, tuple), repr(ratio) length = len(sequence) counts = _math.partition_integer_by_ratio(length, ratio) parts = partition_by_counts(sequence, counts, cyclic=False, overhang=_enums.EXACT) return parts
[docs]def partition_by_ratio_of_weights(sequence, weights) -> list: """ Partitions ``sequence`` by ratio of ``weights``. .. container:: example >>> ratio = (1, 1, 1) >>> sequence = list(10 * [1]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [1, 1, 1] [1, 1, 1, 1] [1, 1, 1] .. container:: example >>> ratio = (1, 1, 1, 1) >>> sequence = list(10 * [1]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [1, 1, 1] [1, 1] [1, 1, 1] [1, 1] .. container:: example >>> ratio = (2, 2, 3) >>> sequence = list(10 * [1]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [1, 1, 1] [1, 1, 1] [1, 1, 1, 1] .. container:: example >>> ratio = (3, 2, 2) >>> sequence = list(10 * [1]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [1, 1, 1, 1] [1, 1, 1] [1, 1, 1] .. container:: example >>> ratio = (1, 1) >>> items = [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2] >>> sequence = list(items) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [1, 1, 1, 1, 1, 1, 2, 2] [2, 2, 2, 2] .. container:: example >>> ratio = (1, 1, 1) >>> items = [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2] >>> sequence = list(items) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [1, 1, 1, 1, 1, 1] [2, 2, 2] [2, 2, 2] .. container:: example >>> ratio = (1, 1, 1) >>> sequence = list([5, 5]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [5] [5] [] .. container:: example >>> ratio = (1, 1, 1, 1) >>> sequence = list([5, 5]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [5] [] [5] [] .. container:: example >>> ratio = (2, 2, 3) >>> sequence = list([5, 5]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [5] [5] [] .. container:: example >>> ratio = (3, 2, 2) >>> sequence = list([5, 5]) >>> sequence = abjad.sequence.partition_by_ratio_of_weights(sequence, ratio) >>> for item in sequence: ... item ... [5] [5] [] Rounded weight-proportions of sequences returned equal to rounded ``weights``. Returns list of ``sequence`` types. """ list_weight = _math.weight(sequence) weights_parts = _math.partition_integer_by_ratio(list_weight, weights) cumulative_weights = _math.cumulative_sums(weights_parts, start=None) items = [] sublist: list[typing.Any] = [] items.append(sublist) current_cumulative_weight = cumulative_weights.pop(0) for item in sequence: if not isinstance(item, int | float | fractions.Fraction): raise TypeError(f"must be number: {item!r}.") sublist.append(item) while current_cumulative_weight <= _math.weight(flatten(items, depth=-1)): try: current_cumulative_weight = cumulative_weights.pop(0) sublist = [] items.append(sublist) except IndexError: break result = [type(sequence)(_) for _ in items] return result
[docs]def partition_by_weights( sequence, weights, *, cyclic: bool = False, overhang: bool = False, allow_part_weights=_enums.EXACT, ) -> list: r""" Partitions ``sequence`` by ``weights`` exactly. >>> sequence = [3, 3, 3, 3, 4, 4, 4, 4, 5] .. container:: example Partitions sequence once by weights with overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [3, 9], ... cyclic=False, ... overhang=False, ... ): ... item ... [3] [3, 3, 3] .. container:: example Partitions sequence once by weights. Allows overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [3, 9], ... cyclic=False, ... overhang=True, ... ): ... item ... [3] [3, 3, 3] [4, 4, 4, 4, 5] .. container:: example Partitions sequence cyclically by weights: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [12], ... cyclic=True, ... overhang=False, ... ): ... item ... [3, 3, 3, 3] [4, 4, 4] .. container:: example Partitions sequence cyclically by weights. Allows overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [12], ... cyclic=True, ... overhang=True, ... ): ... item ... [3, 3, 3, 3] [4, 4, 4] [4, 5] >>> sequence = list([3, 3, 3, 3, 4, 4, 4, 4, 5, 5]) .. container:: example Partitions sequence once by weights. Allows part weights to be just less than specified: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 4], ... cyclic=False, ... overhang=False, ... allow_part_weights=abjad.LESS, ... ): ... item ... [3, 3, 3] [3] .. container:: example Partitions sequence once by weights. Allows part weights to be just less than specified. Allows overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 4], ... cyclic=False, ... overhang=True, ... allow_part_weights=abjad.LESS, ... ): ... item ... [3, 3, 3] [3] [4, 4, 4, 4, 5, 5] .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just less than specified: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 5], ... cyclic=True, ... overhang=False, ... allow_part_weights=abjad.LESS, ... ): ... item ... [3, 3, 3] [3] [4, 4] [4] [4, 5] [5] .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just less than specified. Allows overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 5], ... cyclic=True, ... overhang=True, ... allow_part_weights=abjad.LESS, ... ): ... item ... [3, 3, 3] [3] [4, 4] [4] [4, 5] [5] >>> sequence = list([3, 3, 3, 3, 4, 4, 4, 4, 5, 5]) .. container:: example Partitions sequence once by weights. Allow part weights to be just more than specified: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 4], ... cyclic=False, ... overhang=False, ... allow_part_weights=abjad.MORE, ... ): ... item ... [3, 3, 3, 3] [4] .. container:: example Partitions sequence once by weights. Allows part weights to be just more than specified. Allows overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 4], ... cyclic=False, ... overhang=True, ... allow_part_weights=abjad.MORE, ... ): ... item ... [3, 3, 3, 3] [4] [4, 4, 4, 5, 5] .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just more than specified: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 4], ... cyclic=True, ... overhang=False, ... allow_part_weights=abjad.MORE, ... ): ... item ... [3, 3, 3, 3] [4] [4, 4, 4] [5] .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just more than specified. Allows overhang: >>> for item in abjad.sequence.partition_by_weights( ... sequence, ... [10, 4], ... cyclic=True, ... overhang=True, ... allow_part_weights=abjad.MORE, ... ): ... item ... [3, 3, 3, 3] [4] [4, 4, 4] [5] [5] Returns list of ``sequence`` types. """ if allow_part_weights is _enums.EXACT: candidate = type(sequence)(sequence) candidate = split(candidate, weights, cyclic=cyclic, overhang=overhang) flattened_candidate = flatten(candidate, depth=-1) if flattened_candidate == sequence[: len(flattened_candidate)]: return candidate else: raise Exception("can not partition exactly.") elif allow_part_weights is _enums.MORE: if not cyclic: result = _partition_sequence_once_by_weights_at_least( sequence, weights, overhang=overhang ) else: result = _partition_sequence_cyclically_by_weights_at_least( sequence, weights, overhang=overhang ) elif allow_part_weights is _enums.LESS: if not cyclic: result = _partition_sequence_once_by_weights_at_most( sequence, weights, overhang=overhang ) else: result = _partition_sequence_cyclically_by_weights_at_most( sequence, weights, overhang=overhang ) else: message = "allow_part_weights must be ordinal constant: {!r}." message = message.format(allow_part_weights) raise ValueError(message) return result
[docs]def split(sequence, weights, *, cyclic: bool = False, overhang: bool = False) -> list: r""" Splits ``sequence`` by ``weights``. Splits sequence cyclically by weights with overhang: .. container:: example >>> sequence = list([10, -10, 10, -10]) >>> for part in abjad.sequence.split( ... sequence, ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ): ... part ... [3] [7, -8] [-2, 1] [3] [6, -9] [-1] .. container:: example Splits sequence once by weights with overhang: >>> for part in abjad.sequence.split( ... sequence, ... (3, 15, 3), ... cyclic=False, ... overhang=True, ... ): ... part ... [3] [7, -8] [-2, 1] [9, -10] .. container:: example Splits sequence once by weights without overhang: >>> for part in abjad.sequence.split( ... sequence, ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ): ... part ... [3] [7, -8] [-2, 1] .. container:: example REGRESSION. Splits sequence of durations cyclically by weights with overhang; then expresses durations as pairs with denominator: >>> sequence = list([ ... abjad.Duration(20, 2), ... abjad.Duration(-20, 2), ... abjad.Duration(20, 2), ... abjad.Duration(-20, 2), ... ]) >>> for part in abjad.sequence.split( ... sequence, ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ): ... [abjad.duration.with_denominator(_, 2) for _ in part] ... [(6, 2)] [(14, 2), (-16, 2)] [(-4, 2), (2, 2)] [(6, 2)] [(12, 2), (-18, 2)] [(-2, 2)] Returns list of ``sequence`` types. """ result = [] current_index = 0 current_piece: list[typing.Any] = [] if cyclic: weights = repeat_to_weight( weights, _math.weight(sequence), allow_total=_enums.LESS ) for weight in weights: current_piece_weight = _math.weight(current_piece) while current_piece_weight < weight: current_piece.append(sequence[current_index]) current_index += 1 current_piece_weight = _math.weight(current_piece) if current_piece_weight == weight: current_piece_ = type(sequence)(current_piece) result.append(current_piece_) current_piece = [] elif weight < current_piece_weight: overage = current_piece_weight - weight current_last_element = current_piece.pop(-1) needed = abs(current_last_element) - overage needed *= _math.sign(current_last_element) current_piece.append(needed) current_piece_ = type(sequence)(current_piece) result.append(current_piece_) overage *= _math.sign(current_last_element) current_piece = [overage] if overhang: last_piece = current_piece last_piece.extend(sequence[current_index:]) if last_piece: last_piece_ = type(sequence)(last_piece) result.append(last_piece_) return result
[docs]def filter(sequence, predicate=None): """ Filters ``sequence`` by callable ``predicate``. .. container:: example By length: >>> sequence = [[1], [2, 3, [4]], [5], [6, 7, [8]]] >>> abjad.sequence.filter(sequence, lambda _: len(_) == 1) [[1], [5]] By duration: >>> staff = abjad.Staff("c'4. d'8 e'4. f'8 g'2") >>> sequence = list(staff) >>> abjad.sequence.filter( ... sequence, lambda _: _.written_duration == abjad.Duration(1, 8) ... ) [Note("d'8"), Note("f'8")] Returns ``sequence`` type. """ if predicate is None: return sequence[:] items = [] for item in sequence: if predicate(item): items.append(item) return type(sequence)(items)
# creates an iterator that can generate a flattened list, # descending down into child elements to a depth given in the arguments. # note that depth < 0 is effectively equivalent to infinity. def _flatten_helper(sequence, classes, depth): if not isinstance(sequence, classes): yield sequence elif depth == 0: for item in sequence: yield item else: for item in sequence: # flatten an iterable by one level depth_ = depth - 1 for item_ in _flatten_helper(item, classes, depth_): yield item_
[docs]def flatten(sequence, *, classes=None, depth: int = 1): r""" Flattens ``sequence``. .. container:: example Flattens sequence: >>> sequence = [1, [2, 3, [4]], 5, [6, 7, [8]]] >>> abjad.sequence.flatten(sequence) [1, 2, 3, [4], 5, 6, 7, [8]] Flattens sequence to depth 2: >>> sequence = [1, [2, 3, [4]], 5, [6, 7, [8]]] >>> abjad.sequence.flatten(sequence, depth=2) [1, 2, 3, 4, 5, 6, 7, 8] Flattens sequence to depth -1: >>> sequence = [1, [2, 3, [4]], 5, [6, 7, [8]]] >>> abjad.sequence.flatten(sequence, depth=-1) [1, 2, 3, 4, 5, 6, 7, 8] Flattens tuples in sequence only: >>> sequence = ["ab", "cd", ("ef", "gh"), ("ij", "kl")] >>> abjad.sequence.flatten(sequence, classes=(tuple, list)) ['ab', 'cd', 'ef', 'gh', 'ij', 'kl'] Returns ``sequence`` type. """ if classes is None: classes = (collections.abc.Sequence,) items = _flatten_helper(sequence, classes, depth) return type(sequence)(items)
[docs]def group_by(sequence, predicate=None) -> list: """ Groups ``sequence`` items by value of items. .. container:: example >>> items = [0, 0, -1, -1, 2, 3, -5, 1, 1, 5, -5] >>> sequence = list(items) >>> for item in abjad.sequence.group_by(sequence): ... item ... [0, 0] [-1, -1] [2] [3] [-5] [1, 1] [5] [-5] .. container:: example >>> staff = abjad.Staff("c'8 d' d' e' e' e'") >>> predicate = lambda _: abjad.PitchSet([_]) >>> sequence = list(staff) >>> for item in abjad.sequence.group_by(sequence, predicate): ... item ... [Note("c'8")] [Note("d'8"), Note("d'8")] [Note("e'8"), Note("e'8"), Note("e'8")] Returns list of ``sequence`` types. """ items = [] if predicate is None: pairs = itertools.groupby(sequence, lambda _: _) for count, group in pairs: item = type(sequence)(group) items.append(item) else: pairs = itertools.groupby(sequence, predicate) for count, group in pairs: item = type(sequence)(group) items.append(item) return items
[docs]def has_duplicates(sequence): """ Is true when ``sequence`` has duplicates. .. container:: >>> abjad.sequence.has_duplicates([0, 1, 2, 3, 4]) False >>> abjad.sequence.has_duplicates([0, 1, 2, 3, 3]) True >>> abjad.sequence.has_duplicates([0, 1, 0, 1, 0]) True """ return len(set(sequence)) < len(sequence)
[docs]def is_decreasing(sequence, *, strict: bool = True) -> bool: """ Is true when ``sequence`` is decreasing. Is true when sequence is strictly decreasing: .. container:: example >>> abjad.sequence.is_decreasing([5, 4, 3, 2, 1, 0], strict=True) True >>> abjad.sequence.is_decreasing([3, 3, 3, 2, 1, 0], strict=True) False >>> abjad.sequence.is_decreasing([3, 3, 3, 3, 3, 3], strict=True) False >>> abjad.sequence.is_decreasing([], strict=True) True .. container:: example Is true when sequence decreases monotonically: >>> abjad.sequence.is_decreasing([5, 4, 3, 2, 1, 0], strict=False) True >>> abjad.sequence.is_decreasing([3, 3, 3, 2, 1, 0], strict=False) True >>> abjad.sequence.is_decreasing([3, 3, 3, 3, 3, 3], strict=False) True >>> abjad.sequence.is_decreasing([], strict=False) True """ if strict: try: previous = None for current in sequence: if previous is not None: if not current < previous: return False previous = current return True except TypeError: return False else: try: previous = None for current in sequence: if previous is not None: if not current <= previous: return False previous = current return True except TypeError: return False
[docs]def is_increasing(sequence, *, strict: bool = True) -> bool: """ Is true when ``sequence`` is increasing. Is true when sequence is strictly increasing: .. container:: example >>> abjad.sequence.is_increasing([0, 1, 2, 3, 4, 5], strict=True) True >>> abjad.sequence.is_increasing([0, 1, 2, 3, 3, 3], strict=True) False >>> abjad.sequence.is_increasing([3, 3, 3, 3, 3, 3], strict=True) False >>> abjad.sequence.is_increasing([], strict=True) True .. container:: example Is true when sequence increases monotonically: >>> abjad.sequence.is_increasing([0, 1, 2, 3, 4, 5], strict=False) True >>> abjad.sequence.is_increasing([0, 1, 2, 3, 3, 3], strict=False) True >>> abjad.sequence.is_increasing([3, 3, 3, 3, 3, 3], strict=False) True >>> abjad.sequence.is_increasing([], strict=False) True """ if strict: try: previous = None for current in sequence: if previous is not None: if not previous < current: return False previous = current return True except TypeError: return False else: try: previous = None for current in sequence: if previous is not None: if not previous <= current: return False previous = current return True except TypeError: return False
[docs]def is_permutation(sequence) -> bool: """ Is true when ``sequence`` is a permutation. .. container:: example >>> abjad.sequence.is_permutation([4, 5, 0, 3, 2, 1]) True >>> abjad.sequence.is_permutation([1, 1, 5, 3, 2, 1]) False """ return tuple(sorted(sequence)) == tuple(range(len(sequence)))
[docs]def is_repetition_free(sequence) -> bool: """ Is true when ``sequence`` is repetition-free. .. container:: example >>> abjad.sequence.is_repetition_free([0, 1, 2, 6, 7, 8]) True >>> abjad.sequence.is_repetition_free([]) True >>> abjad.sequence.is_repetition_free([0, 1, 2, 2, 7, 8]) False """ try: for left, right in nwise(sequence): if left == right: return False return True except TypeError: return False
[docs]def join(sequence): """ Join subsequences in ``sequence``. .. container:: example >>> items = [(1, 2, 3), (), (4, 5), (), (6,)] >>> sequence = list(items) >>> sequence [(1, 2, 3), (), (4, 5), (), (6,)] >>> abjad.sequence.join(sequence) [(1, 2, 3, 4, 5, 6)] Returns ``sequence`` type. """ if not sequence: return type(sequence)() item = sequence[0] for item_ in sequence[1:]: item += item_ return type(sequence)([item])
[docs]def nwise( sequence, n: int = 2, *, cyclic: bool = False, wrapped: bool = False ) -> typing.Iterator: """ Iterates ``sequence`` ``n`` items at a time. Iterates items 2 at a time: .. container:: example >>> sequence = list(range(10)) >>> for item in abjad.sequence.nwise(sequence): ... item ... [0, 1] [1, 2] [2, 3] [3, 4] [4, 5] [5, 6] [6, 7] [7, 8] [8, 9] Iterates items 3 at a time: >>> sequence = list(range(10)) >>> for item in abjad.sequence.nwise(sequence, n=3): ... item ... [0, 1, 2] [1, 2, 3] [2, 3, 4] [3, 4, 5] [4, 5, 6] [5, 6, 7] [6, 7, 8] [7, 8, 9] Iterates items 2 at a time. Wraps around at end: >>> sequence = list(range(10)) >>> for item in abjad.sequence.nwise(sequence, n=2, wrapped=True): ... item ... [0, 1] [1, 2] [2, 3] [3, 4] [4, 5] [5, 6] [6, 7] [7, 8] [8, 9] [9, 0] Iterates items 3 at a time. Wraps around at end: >>> sequence = list(range(10)) >>> for item in abjad.sequence.nwise(sequence, n=3, wrapped=True): ... item ... [0, 1, 2] [1, 2, 3] [2, 3, 4] [3, 4, 5] [4, 5, 6] [5, 6, 7] [6, 7, 8] [7, 8, 9] [8, 9, 0] [9, 0, 1] Iterates items 2 at a time. Cycles indefinitely: >>> sequence = list(range(10)) >>> pairs = abjad.sequence.nwise(sequence, n=2, cyclic=True) >>> for _ in range(15): ... next(pairs) ... [0, 1] [1, 2] [2, 3] [3, 4] [4, 5] [5, 6] [6, 7] [7, 8] [8, 9] [9, 0] [0, 1] [1, 2] [2, 3] [3, 4] [4, 5] Returns infinite generator. Iterates items 3 at a time. Cycles indefinitely: >>> sequence = list(range(10)) >>> triples = abjad.sequence.nwise(sequence, n=3, cyclic=True) >>> for _ in range(15): ... next(triples) ... [0, 1, 2] [1, 2, 3] [2, 3, 4] [3, 4, 5] [4, 5, 6] [5, 6, 7] [6, 7, 8] [7, 8, 9] [8, 9, 0] [9, 0, 1] [0, 1, 2] [1, 2, 3] [2, 3, 4] [3, 4, 5] [4, 5, 6] Returns infinite generator. Iterates items 1 at a time: >>> sequence = list(range(10)) >>> for item in abjad.sequence.nwise(sequence, n=1): ... item ... [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] Ignores ``wrapped`` when ``cyclic=True``. """ if cyclic: item_buffer = [] long_enough = False for item in sequence: item_buffer.append(item) if not long_enough: if n <= len(item_buffer): long_enough = True if long_enough: yield type(sequence)(item_buffer[-n:]) len_sequence = len(item_buffer) current = len_sequence - n + 1 while True: output = [] for local_offset in range(n): index = (current + local_offset) % len_sequence output.append(item_buffer[index]) yield type(sequence)(output) current += 1 current %= len_sequence elif wrapped: first_n_minus_1: list[typing.Any] = [] item_buffer = [] for item in sequence: item_buffer.append(item) if len(item_buffer) == n: yield type(sequence)(item_buffer) item_buffer.pop(0) if len(first_n_minus_1) < n - 1: first_n_minus_1.append(item) item_buffer = item_buffer + first_n_minus_1 if item_buffer: for x in range(n - 1): stop = x + n yield type(sequence)(item_buffer[x:stop]) else: item_buffer = [] for item in sequence: item_buffer.append(item) if len(item_buffer) == n: yield type(sequence)(item_buffer) item_buffer.pop(0)
[docs]def permute(sequence, permutation): r""" Permutes ``sequence`` by ``permutation``. .. container:: example >>> abjad.sequence.permute([10, 11, 12, 13, 14, 15], [5, 4, 0, 1, 2, 3]) [15, 14, 10, 11, 12, 13] >>> abjad.sequence.permute([11, 12, 13, 14], [1, 0, 3, 2]) [12, 11, 14, 13] .. container:: exception Raises exception when lengths do not match: >>> abjad.sequence.permute([1, 2, 3, 4, 5, 6], [3, 0, 1, 2]) Traceback (most recent call last): ... ValueError: permutation [3, 0, 1, 2] must match length of [1, 2, 3, 4, 5, 6]. Returns ``sequence`` type. """ permutation = type(sequence)(permutation) if not is_permutation(permutation): raise ValueError(f"must be permutation: {permutation!r}.") if len(permutation) != len(sequence): message = f"permutation {permutation!r} must match length of {sequence !r}." raise ValueError(message) result = [] for i, item in enumerate(sequence): j = permutation[i] item_ = sequence[j] result.append(item_) return type(sequence)(result)
[docs]def remove(sequence, pattern): """ Removes ``sequence`` items at indices specified by ``pattern``. .. container:: example >>> sequence = list(range(15)) >>> abjad.sequence.remove(sequence, abjad.index([2, 3])) [0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Removes elements and indices -2 and -3: >>> abjad.sequence.remove(sequence, abjad.index([-2, -3])) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14] >>> abjad.sequence.remove(sequence, abjad.index([2, 3], 4)) [0, 1, 4, 5, 8, 9, 12, 13] >>> abjad.sequence.remove(sequence, abjad.index([-2, -3], 4)) [2, 3, 6, 7, 10, 11, 14] >>> abjad.sequence.remove(sequence, abjad.index([])) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] >>> abjad.sequence.remove(sequence, abjad.index([97, 98, 99])) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Removes no elements: >>> abjad.sequence.remove(sequence, abjad.index([-97, -98, -99])) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Returns ``sequence`` type. """ items = [] length = len(sequence) indices = pattern.indices period = pattern.period or length if indices is None: indices = range(length) new_indices = [] for i in indices: if length < abs(i): continue if i < 0: i = length + i i = i % period new_indices.append(i) indices = new_indices indices.sort() for i, item in enumerate(sequence): if i % period not in indices: items.append(item) return type(sequence)(items)
[docs]def remove_repeats(sequence): """ Removes repeats from ``sequence``. .. container:: example >>> abjad.sequence.remove_repeats([31, 31, 35, 35, 31, 31, 31, 31, 35]) [31, 35, 31, 35] Returns ``sequence`` type. """ items = [sequence[0]] for item in sequence[1:]: if item != items[-1]: items.append(item) return type(sequence)(items)
[docs]def repeat(sequence, n: int = 1) -> list: r""" Repeats ``sequence``, to a total of ``n`` copies. .. container:: example >>> abjad.sequence.repeat([1, 2, 3], n=0) [] >>> abjad.sequence.repeat([1, 2, 3], n=1) [[1, 2, 3]] >>> abjad.sequence.repeat([1, 2, 3], n=2) [[1, 2, 3], [1, 2, 3]] Returns list of ``sequence`` types. """ sequences = [] for i in range(n): sequences.append(sequence[:]) return sequences
[docs]def repeat_to_length(sequence, length: int = 0, *, start: int = 0): """ Repeats ``sequence`` to ``length``. .. container:: example Repeats list to length 11: >>> abjad.sequence.repeat_to_length([0, 1, 2, 3, 4], 11) [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0] >>> abjad.sequence.repeat_to_length([0, 1, 2, 3, 4], 11, start=2) [2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2] >>> abjad.sequence.repeat_to_length([0, -1, -2, -3, -4], 11) [0, -1, -2, -3, -4, 0, -1, -2, -3, -4, 0] >>> abjad.sequence.repeat_to_length([0, -1, -2, -3, -4], 0) [] >>> abjad.sequence.repeat_to_length([1, 2, 3], 10, start=100) [2, 3, 1, 2, 3, 1, 2, 3, 1, 2] Returns ``sequence`` type. """ assert _math.is_nonnegative_integer(length), repr(length) assert len(sequence), repr(sequence) items = [] start %= len(sequence) stop_index = start + length repetitions = int(math.ceil(float(stop_index) / len(sequence))) for i in range(repetitions): for item in sequence: items.append(item) return type(sequence)(items[start:stop_index])
[docs]def repeat_to_weight(sequence, weight, *, allow_total=_enums.EXACT): """ Repeats ``sequence`` to ``weight``. .. container:: example Repeats sequence to weight of 23 exactly: >>> abjad.sequence.repeat_to_weight([5, -5, -5], 23) [5, -5, -5, 5, -3] Repeats sequence to weight of 23 more: >>> abjad.sequence.repeat_to_weight([5, -5, -5], 23, allow_total=abjad.MORE) [5, -5, -5, 5, -5] Repeats sequence to weight of 23 or less: >>> abjad.sequence.repeat_to_weight([5, -5, -5], 23, allow_total=abjad.LESS) [5, -5, -5, 5] >>> sequence = [abjad.Duration(3, 16)] >>> weight = abjad.Duration(5, 4) >>> sequence = abjad.sequence.repeat_to_weight(sequence, weight) >>> sum(sequence) Duration(5, 4) >>> [abjad.duration.with_denominator(_, 16) for _ in sequence] [(3, 16), (3, 16), (3, 16), (3, 16), (3, 16), (3, 16), (2, 16)] Returns ``sequence`` type. """ assert 0 <= weight if allow_total is _enums.EXACT: sequence_weight = _math.weight(sequence) complete_repetitions = int(math.ceil(float(weight) / float(sequence_weight))) items = list(sequence) items = complete_repetitions * items overage = complete_repetitions * sequence_weight - weight for item in reversed(items): if 0 < overage: element_weight = abs(item) candidate_overage = overage - element_weight if 0 <= candidate_overage: overage = candidate_overage items.pop() else: absolute_amount_to_keep = element_weight - overage assert 0 < absolute_amount_to_keep signed_amount_to_keep = absolute_amount_to_keep signed_amount_to_keep *= _math.sign(item) items.pop() items.append(signed_amount_to_keep) break else: break elif allow_total is _enums.LESS: items = [sequence[0]] i = 1 while _math.weight(items) < weight: items.append(sequence[i % len(sequence)]) i += 1 if weight < _math.weight(items): items = items[:-1] return type(sequence)(items) elif allow_total is _enums.MORE: items = [sequence[0]] i = 1 while _math.weight(items) < weight: items.append(sequence[i % len(sequence)]) i += 1 return type(sequence)(items) else: raise ValueError(f"is not an ordinal value constant: {allow_total!r}.") return type(sequence)(items)
[docs]def replace(sequence, old, new): """ Replaces every ``old``-valued item in ``sequence`` with ``new``. .. container:: example >>> sequence = [0, 2, 3, 0, 2, 3, 0, 2, 3] >>> abjad.sequence.replace(sequence, 0, 1) [1, 2, 3, 1, 2, 3, 1, 2, 3] Returns ``sequence`` type. """ items = [] for item in sequence: if item == old: new_copy = copy.copy(new) items.append(new_copy) else: items.append(item) return type(sequence)(items)
[docs]def replace_at(sequence, indices, new_material): """ Replaces items at ``indices`` with ``new_material``. .. container:: example Replaces items at indices 0, 2, 4, 6: >>> sequence = list(range(16)) >>> abjad.sequence.replace_at( ... sequence, ... ([0], 2), ... (['A', 'B', 'C', 'D'], None), ... ) ['A', 1, 'B', 3, 'C', 5, 'D', 7, 8, 9, 10, 11, 12, 13, 14, 15] Replaces elements at indices 0, 1, 8, 13: >>> sequence = list(range(16)) >>> abjad.sequence.replace_at( ... sequence, ... ([0, 1, 8, 13], None), ... (['A', 'B', 'C', 'D'], None), ... ) ['A', 'B', 2, 3, 4, 5, 6, 7, 'C', 9, 10, 11, 12, 'D', 14, 15] Replaces every item at even index: >>> sequence = list(range(16)) >>> abjad.sequence.replace_at( ... sequence, ... ([0], 2), ... (['*'], 1), ... ) ['*', 1, '*', 3, '*', 5, '*', 7, '*', 9, '*', 11, '*', 13, '*', 15] Replaces every element at an index congruent to 0 (mod 6) with ``'A'``; replaces every element at an index congruent to 2 (mod 6) with ``'B'``: >>> sequence = list(range(16)) >>> abjad.sequence.replace_at( ... sequence, ... ([0], 2), ... (['A', 'B'], 3), ... ) ['A', 1, 'B', 3, 4, 5, 'A', 7, 'B', 9, 10, 11, 'A', 13, 'B', 15] Returns ``sequence`` type. """ assert isinstance(indices, collections.abc.Sequence) assert len(indices) == 2 index_values, index_period = indices assert isinstance(index_values, collections.abc.Sequence) index_values = list(index_values) assert isinstance(index_period, int | type(None)) assert isinstance(new_material, collections.abc.Sequence) assert len(new_material) == 2 material_values, material_period = new_material assert isinstance(material_values, collections.abc.Sequence) material_values = list(material_values) assert isinstance(material_period, int | type(None)) maxsize = sys.maxsize if index_period is None: index_period = maxsize if material_period is None: material_period = maxsize items = [] material_index = 0 for index, item in enumerate(sequence): if index % index_period in index_values: try: cyclic_material_index = material_index % material_period material_value = material_values[cyclic_material_index] items.append(material_value) except IndexError: items.append(item) material_index += 1 else: items.append(item) return type(sequence)(items)
[docs]def retain_pattern(sequence, pattern): """ Retains ``sequence`` items at indices matching ``pattern``. .. container:: example >>> sequence = list(range(10)) >>> abjad.sequence.retain_pattern(sequence, abjad.index_all()) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> abjad.sequence.retain_pattern(sequence, abjad.index([2, 3])) [2, 3] >>> abjad.sequence.retain_pattern(sequence, abjad.index([-2, -3])) [7, 8] >>> abjad.sequence.retain_pattern(sequence, abjad.index([2, 3], 4)) [2, 3, 6, 7] >>> abjad.sequence.retain_pattern(sequence, abjad.index([-2, -3], 4)) [0, 3, 4, 7, 8] >>> abjad.sequence.retain_pattern(sequence, abjad.index([97, 98, 99])) [] >>> abjad.sequence.retain_pattern(sequence, abjad.index([-97, -98, -99])) [] Returns ``sequence`` type. """ length = len(sequence) items = [] for i, item in enumerate(sequence): if pattern.matches_index(i, length): items.append(item) return type(sequence)(items)
[docs]def reverse(sequence, *, recurse: bool = False): r""" Reverses ``sequence``. .. container:: example Reverses sequence: >>> sequence = [[1, 2], 3, [4, 5]] >>> abjad.sequence.reverse(sequence) [[4, 5], 3, [1, 2]] Reverses recursively: >>> segment_1 = abjad.PitchClassSegment([1, 2]) >>> pitch = abjad.NumberedPitch(3) >>> segment_2 = abjad.PitchClassSegment([4, 5]) >>> sequence = list([segment_1, pitch, segment_2]) >>> for item in abjad.sequence.reverse(sequence, recurse=True): ... item ... PitchClassSegment([5, 4]) NumberedPitch(3) PitchClassSegment([2, 1]) Returns ``sequence`` type. """ if not recurse: return type(sequence)(reversed(sequence)) def _reverse_helper(item): if isinstance(item, collections.abc.Iterable): subitems_ = [_reverse_helper(_) for _ in reversed(item)] return type(item)(subitems_) else: return item items = _reverse_helper(sequence[:]) return type(sequence)(items)
[docs]def rotate(sequence, n: int = 0): r""" Rotates ``sequence`` by index ``n``. .. container:: example Rotates sequence to the right: >>> sequence = list(range(10)) >>> abjad.sequence.rotate(sequence, n=4) [6, 7, 8, 9, 0, 1, 2, 3, 4, 5] Rotates sequence to the left: >>> sequence = list(range(10)) >>> abjad.sequence.rotate(sequence, n=-3) [3, 4, 5, 6, 7, 8, 9, 0, 1, 2] Rotates sequence to neither the right nor the left: >>> sequence = list(range(10)) >>> abjad.sequence.rotate(sequence, n=0) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Returns ``sequence`` type. """ n = n or 0 items = [] if len(sequence): n = n % len(sequence) for item in sequence[-n : len(sequence)] + sequence[:-n]: items.append(item) return type(sequence)(items)
[docs]def sum_by_sign(sequence, *, sign=(-1, 0, 1)): """ Sums consecutive ``sequence`` items by ``sign``. >>> sequence = [0, 0, -1, -1, 2, 3, -5, 1, 2, 5, -5, -6] >>> abjad.sequence.sum_by_sign(sequence) [0, -2, 5, -5, 8, -11] >>> abjad.sequence.sum_by_sign(sequence, sign=[-1]) [0, 0, -2, 2, 3, -5, 1, 2, 5, -11] >>> abjad.sequence.sum_by_sign(sequence, sign=[0]) [0, -1, -1, 2, 3, -5, 1, 2, 5, -5, -6] >>> abjad.sequence.sum_by_sign(sequence, sign=[1]) [0, 0, -1, -1, 5, -5, 8, -5, -6] >>> abjad.sequence.sum_by_sign(sequence, sign=[-1, 0]) [0, -2, 2, 3, -5, 1, 2, 5, -11] >>> abjad.sequence.sum_by_sign(sequence, sign=[-1, 1]) [0, 0, -2, 5, -5, 8, -11] >>> abjad.sequence.sum_by_sign(sequence, sign=[0, 1]) [0, -1, -1, 5, -5, 8, -5, -6] >>> abjad.sequence.sum_by_sign(sequence, sign=[-1, 0, 1]) [0, -2, 5, -5, 8, -11] Sums consecutive negative elements when ``-1`` in ``sign``. Sums consecutive zero-valued elements when ``0`` in ``sign``. Sums consecutive positive elements when ``1`` in ``sign``. Returns ``sequence`` type. """ items = [] generator = itertools.groupby(sequence, _math.sign) for current_sign, group in generator: if current_sign in sign: items.append(sum(list(group))) else: for item in group: items.append(item) return type(sequence)(items)
[docs]def truncate(sequence, *, sum_=None, weight=None): """ Truncates ``sequence``. >>> sequence = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] .. container:: example Truncates sequence to weights ranging from 1 to 10: >>> for weight in range(1, 11): ... result = abjad.sequence.truncate(sequence, weight=weight) ... print(weight, result) ... 1 [-1] 2 [-1, 1] 3 [-1, 2] 4 [-1, 2, -1] 5 [-1, 2, -2] 6 [-1, 2, -3] 7 [-1, 2, -3, 1] 8 [-1, 2, -3, 2] 9 [-1, 2, -3, 3] 10 [-1, 2, -3, 4] Truncates sequence to sums ranging from 1 to 10: >>> for sum_ in range(1, 11): ... result = abjad.sequence.truncate(sequence, sum_=sum_) ... print(sum_, result) ... 1 [-1, 2] 2 [-1, 2, -3, 4] 3 [-1, 2, -3, 4, -5, 6] 4 [-1, 2, -3, 4, -5, 6, -7, 8] 5 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] 6 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] 7 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] 8 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] 9 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] 10 [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] Truncates sequence to zero weight: >>> abjad.sequence.truncate(sequence, weight=0) [] Truncates sequence to zero sum: >>> abjad.sequence.truncate(sequence, sum_=0) [] Ignores ``sum`` when ``weight`` and ``sum`` are both set. Raises value error on negative ``sum``. Returns ``sequence`` type. """ if weight is not None: assert 0 <= weight, repr(weight) items = [] if 0 < weight: total = 0 for item in sequence: total += abs(item) if total < weight: items.append(item) else: sign = _math.sign(item) trimmed_part = weight - _math.weight(items) trimmed_part *= sign items.append(trimmed_part) break elif sum_ is not None: assert 0 <= sum_, repr(sum_) items = [] if 0 < sum_: total = 0 for item in sequence: total += item if total < sum_: items.append(item) else: items.append(sum_ - sum(items)) break return type(sequence)(items)
[docs]def weight(sequence) -> typing.Any: """ Gets weight of ``sequence``. .. container:: example >>> abjad.sequence.weight([]) 0 >>> abjad.sequence.weight([1]) 1 >>> abjad.sequence.weight([1, 2, 3]) 6 >>> abjad.sequence.weight([1, 2, -3]) 6 >>> abjad.sequence.weight([-1, -2, -3]) 6 >>> abjad.sequence.weight([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) 55 >>> abjad.sequence.weight([[1, -7, -7], [1, -8 -8]]) 32 """ weights = [] for item in sequence: if isinstance(item, collections.abc.Iterable): weights.append(weight(item)) else: weights.append(abs(item)) return sum(weights)
[docs]def zip(sequences, *, cyclic: bool = False, truncate: bool = True) -> list[tuple]: """ Zips ``sequences``. Zips cyclically: .. container:: example >>> sequence = [[1, 2, 3], ['a', 'b']] >>> for item in abjad.sequence.zip(sequence, cyclic=True): ... item ... (1, 'a') (2, 'b') (3, 'a') >>> sequence = [[10, 11, 12], [20, 21], [30, 31, 32, 33]] >>> for item in abjad.sequence.zip(sequence, cyclic=True): ... item ... (10, 20, 30) (11, 21, 31) (12, 20, 32) (10, 21, 33) Zips without truncation: >>> sequence = [[1, 2, 3, 4], [11, 12, 13], [21, 22, 23]] >>> for item in abjad.sequence.zip(sequence, truncate=False): ... item ... (1, 11, 21) (2, 12, 22) (3, 13, 23) (4,) .. container:: example Zips strictly: >>> sequences = [[1, 2, 3, 4], [11, 12, 13], [21, 22, 23]] >>> for triple in abjad.sequence.zip(sequences): ... triple ... (1, 11, 21) (2, 12, 22) (3, 13, 23) Returns list of tuples. """ for item in sequences: if not isinstance(item, collections.abc.Iterable): raise Exception(f"must be iterable: {item!r}.") items: list[typing.Any] = [] if cyclic: if not min(len(_) for _ in sequences): return items maximum_length = max([len(_) for _ in sequences]) for i in range(maximum_length): part = [] for item in sequences: index = i % len(item) element = item[index] part.append(element) items.append(part) elif not truncate: maximum_length = max([len(_) for _ in sequences]) for i in range(maximum_length): part = [] for item in sequences: try: part.append(item[i]) except IndexError: pass items.append(part) elif truncate: for item in builtins.zip(*sequences): items.append(item) result = [tuple(_) for _ in items] return result