{"id":19961128,"url":"https://github.com/dense-analysis/ranges","last_synced_at":"2025-05-03T22:30:34.611Z","repository":{"id":57705968,"uuid":"476920172","full_name":"dense-analysis/ranges","owner":"dense-analysis","description":"Range-based algorithms in Go","archived":false,"fork":false,"pushed_at":"2023-09-10T23:19:41.000Z","size":245,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-06-20T08:20:59.305Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dense-analysis.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-02T01:56:26.000Z","updated_at":"2024-01-17T19:23:02.000Z","dependencies_parsed_at":"2024-06-20T07:23:55.585Z","dependency_job_id":"f2c987bb-971e-4769-9859-6b910bc3c51a","html_url":"https://github.com/dense-analysis/ranges","commit_stats":null,"previous_names":["w0rp/ranges"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dense-analysis%2Franges","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dense-analysis%2Franges/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dense-analysis%2Franges/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dense-analysis%2Franges/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dense-analysis","download_url":"https://codeload.github.com/dense-analysis/ranges/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224374785,"owners_count":17300691,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T02:06:28.672Z","updated_at":"2024-11-13T02:06:29.345Z","avatar_url":"https://github.com/dense-analysis.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ranges\n\n[![Go](https://img.shields.io/badge/pkg-%2311AB00.svg?style=for-the-badge\u0026logo=go\u0026labelColor=555555\u0026logoColor=white)](https://pkg.go.dev/github.com/dense-analysis/ranges) [![CI](https://img.shields.io/github/actions/workflow/status/dense-analysis/ranges/go.yml?branch=master\u0026style=for-the-badge\u0026label=ci\u0026logo=github)](https://github.com/dense-analysis/ranges/actions/workflows/go.yml?query=event%3Apush+branch%3Amaster)\n\n`ranges` implements the nearest implementation of D range-based algorithms in\nGo, for fun and to experiment with what is possible in Go 1.18 and above.\n\nRanges are a concept popular in D and C++ for creating a language by which\ngeneric algorithms can be define which operate on potentially any type of\ncontainer or sequence of data. Instead of redundantly defining the same\nalgorithms over and over again for different types, you instead define how to\ncreate a range that lazily evaluates a given container or sequence in\npotentially many directions, and existing generic algorithms can be applied to\nthe range.\n\n## Primitives\n\nThe library defines the following ranges primitives, as Go interfaces.\n\n* `OutputRange[T any]` - Any type you can write the following operations:\n  * `Put(element T) error` Write to the output range.\n* `InputRange[T any]` - Anything iterable, with the following operations:\n  * `Empty() bool` - Check if a range is empty.\n  * `Front() T` - Get the current element. May panic if an empty range.\n  * `PopFront()` - Remove the front element. May panic if an empty range.\n* `ForwardRange[T any]` - An `InputRange[T]` with additional operations:\n  * `Save() ForwardRange[T]` - Copy the range and its position.\n* `BidirectionalRange[T any]` - `ForwardRange[T]` with additional operations:\n  * `Back() T` - Get the current end element. May panic if an empty range.\n  * `PopBack() T` - Remove the back/end element. May panic if an empty range.\n  * `SaveB() BidirectionalRange[T]` - Save the position in both directions.\n* `RandomAccessRange[T any]` - A `BidirectionalRange[T]` with additional\n  operations:\n  * `Get(index int) T` Return an element of the range. May panic if out of\n    bounds.\n  * `Len() int` - Return the length of the range.\n  * `SaveR() RandomAccessRange[T]` - Save the position with random access.\n\nThe ranges library defines the following types, which may be used as constraints\nfor generic functions.\n\n* `Signed` - Any signed integer primitive type.\n* `Unsigned` - Any signed integer primitive type.\n* `Integer` - Any integer primitive type.\n* `Float` - Any floating point primitive type.\n* `RealNumber` - Any integer or floating point primitive type.\n* `Ordered` - Any primitive type that be ordered.\n\n### Tuple Types\n\nTo express tuples in Go, there are different types declared for different\nnumbers of items. This library has the following:\n\n* `Pair` - Holds 2 values of any mix of types.\n* `Triplet` - Holds 3 values of any mix of types.\n* `Quartet` - Holds 4 values of any mix of types.\n* `Quintet` - Holds 5 values of any mix of types.\n* `Sextet` - Holds 6 values of any mix of types.\n* `Septet` - Holds 7 values of any mix of types.\n* `Octet` - Holds 8 values of any mix of types.\n* `Ennead` - Holds 9 values of any mix of types.\n* `Decade` - Holds 10 values of any mix of types.\n\nFor convenience, every tuple has a `Make` function for creating them, and a\n`Get()` function for returning the values as a Go native tuple, so tuples\ncan be and split into multiple values without redundantly naming types.\n\n```go\n// Inferred as Pair[int, string]\npair := MakePair(1, \"hello\")\n// Split into int and string values.\nnum, str := pair.Get()\n```\n\n## Error Handling\n\nRanges other than `OutputRange` do not include `error` values as part of their\ntypes. Algorithms in this library do not result in runtime errors. They may only\npanic when input to the functions producing the ranges is invalid, or when\nattempting to access elements that do not exist. When you wish to place values\nthat result in errors, make the errors an explicit part of the type of your\nrange such as `InputRange[Pair[T, error]]`.\n\nRemember that in Go you can forward Go's native return tuples as arguments and\nreturn values, and this integrates well with the ranges library tuple types.\nThis can make forwarding errors easier.\n\n```go\nfunc OtherFunc() int, error { /* ... */ }\n\nfunc ReturnsValueAndError() int, error {\n  // Create Pair[int, error]\n  // Go can spread the multiple return into the arguments for us.\n  pair := MakePair(OtherFunc())\n\n  // Return both values.\n  return pair.Get()\n}\n```\n\n## Lazy Evaluation\n\nAll ranges are lazily-evaluated and compute values anew on demand. This means\neach call to `Front()` or `Back()` on a range will return a new value of `T`.\n\nModifications to the returned objects may not be reflected in a container, such\nas returning a copy of a struct instead of a pointer to it. When modification\nto elements of an underlying container is necessary, you should create a range\nof pointers, as in `*T`. This will permit modification of underlying values.\n\nBecause values are computed on the fly, a call to a callback function may be\nexecuted each time `Front()` or `Back()` are called for ranges. You may wish to\ncache the results of computation in a chain of ranges by calling one the `Cache`\nfunctions, including `Cache`, `CacheF`, `CacheB`, and `CacheR`.\n\n## Algorithms\n\nNearly all algorithms accepting or producing `InputRange` can accept or produce\na `ForwardRange` instead by calling a variation of the function with an `F`\nsuffix. Most algorithms that can accept or produce a `ForwardRange` can accept\nor produce a `BidirectionalRange` with a `B` suffix. Some functions such as `Map` can be called with shortcuts for slices\nwith an `S` suffix.\n\n* `operators`\n  * `Lt` - Implements `a \u003c b` for all orderable types.\n  * `Le` - Implements `a \u003c= b` for all orderable types.\n  * `Eq` - Implements `a == b` for all comparable types.\n  * `Ne` - Implements `a != b` for all comparable types.\n  * `Ge` - Implements `a \u003e= b` for all orderable types.\n  * `Gt` - Implements `a \u003e b` for all orderable types.\n* `functional`\n  * `Compose*` - Composes several functions in a sequence, such that\n    `Compose*(f1, ..., fn)(x)` produces `f1(...(fn(x)))`\n  * `Pipe*` - Pipes several functions in a sequence, such that\n    `Pipe*(f1, ..., fn)(x)` produces `fn(...(f1(x)))`\n* `ranges`\n  * `Chain` - Produces an `InputRange` iterating over a slice of ranges.\n  * `Chunks` - Takes `InputRange` chunks of a given size from an `InputRange`.\n  * `Cycle` - Repeats a `ForwardRange` infinitely.\n  * `Drop` - Drops up to a number of elements from the start of a range.\n  * `Enumerate` - Yields elements with indexes (`Pair[int, T]`) from `0`.\n  * `EnumerateN` - `Enumerate` with a provided start index.\n  * `Flatten` - Produces an `InputRange` from a range of ranges.\n  * `FlattenSB` - A special variation to flatten a slice of\n    `BidirectionalRange`s into a `BidirectionalRange.\n  * `FlattenSS` - A special variation to flatten a slice of slices into a\n    `BidirectionalRange.\n  * `FrontTransversal` - Produces an `InputRange` iterating over the first value\n    of every non-empty range in a range of ranges.\n  * `Generate` - Creates an infinite `BidirectionalRange` by calling a function\n    repeatedly.\n  * `Iota` - A `BidirectionalRange` producing values from `0` value up to and\n    excluding an `end` value, incrementing by `1`.\n  * `IotaStart` - `Iota` with a given `start` value to use in place of `0`.\n  * `IotaStep` - `IotaStart` with a `step` value to use in place of `1`.\n  * `Null` - Returns a `BidirectionalRange` that is always empty and consumes\n    zero memory.\n  * `Only` - Returns a `BidirectionalRange` through the arguments provided.\n  * `PadRight` - Produces an `InputRange` with up to `count` items by padding\n    the range with a given value.\n  * `Repeat` - Creates an infinite `BidirectionalRange` repeating a value.\n  * `Retro` - Returns a reversed `BidirectionalRange`.\n  * `RoundRobin` - Produces an `InputRange` iterating over the first value of\n    every non-empty range in a range of ranges in a cycle until all ranges are\n    exhausted.\n  * `Slide` - Produces a `ForwardRange` of chunks of a given `windowSize`\n    stepping forward `1` element at a time.\n  * `SlideStep` - `Slide` with a `stepSize` for stepping over elements.\n  * `Stride` - Produces every `step` element in an `InputRange`.\n  * `Take` - Takes up to a number of elements from a range.\n  * `Tee` - Produces an `InputRange` that produces elements from a given range\n    and outputs values to a given `OutputRange` when elements are popped.\n  * `ZipN` - Produce a range stepping over `N` ranges in parallel. There are\n    several `Zip` functions for different numbers of arguments.\n* `output`\n  * `AssignSink` - Creates an `OutputRange` that assigns values to a given\n    `InputRange` of pointers by dereferencing the pointers.\n  * `NullSink` - Creates an `OutputRange` that discards all data.\n  * `SliceSink` - Creates an `OutputRange` that appends to the given slice.\n* `slices`\n  * `Bytes` - Produces `BidirectionalRange[byte]` from a `string` like `[]byte(s)`\n  * `Runes` - Produces `BidirectionalRange[rune]` from a `string` like `[]rune(s)`\n  * `SliceRange` - Produces `BidirectionalRange[T]` from `[]T`\n  * `SliceRetro` - Produces `BidirectionalRange[T]` from `[]T` in reverse.\n  * `SlicePtrRange` - Produces a `BidirectionalRange[*T]` from `[]T`\n  * `SlicePtrRetro` - Produces a `BidirectionalRange[*T]` from `[]T` in reverse.\n  * `Slice` - Produces `[]T` from `InputRange[T]`\n  * `String` - Produces `string` from `InputRange[rune]`\n* `comparison`\n  * `Among` - Returns `true` if a `value` is equal to any of the `values`\n     according to an `eq` callback.\n  * `AmongEq` - Returns `true` if a `value` is equal to any of the `values`\n     using a simple `==` comparison.\n  * `Cmp` - Steps through two ranges comparing values with a `cmp` function and\n    returns the result if it's nonzero. Returns `-1` or `1` if ranges are\n    different lengths.\n  * `CmpFunc` - Produces a comparison function for all types that support `\u003c`\n    and `\u003e`.\n  * `Equal` - Returns `true` if two ranges are equal according to a comparison\n    defined in a callback.\n  * `EqualComparable` - Returns `true` if two ranges are equal, element by\n    element, for all comparable types.\n  * `IsPermutation` - Returns `true` if two ranges are permutations of each\n    other in `O(m + n)` time by allocating a temporary map.\n  * `IsPermutationNoAlloc` - Returns `true` if two ranges are permutations of\n    each other in `O(m * n)` without allocating a map.\n  * `IsSameLength` - Checks if two ranges are the same length in `O(n)` time.\n  * `Max` - Returns the maximum value among all values given as arguments.\n  * `Min` - Returns the minimum value among all values given as arguments.\n  * `Mismatch` - Eagerly advances all ranges until the first element is found\n    where any two elements are not equal according to a callback.\n* `iteration`\n  * `Cache` - Caches results in an `InputRange` so `Front()` will be called only\n    once per element on the original range.\n  * `CacheF` - Caches results so `Front()` will only be called once per element\n    on the original range, unless the range is saved and traversed over multiple\n    times.\n  * `CacheB` OR `CacheR` - Caching of `BidirectionalRange` and\n    `RandomAccessRange` elements.\n  * `ChunkBy` - Returns an `InputRange` that splits a range into sub-ranges\n    when `cb(a, b)` returns `false`.\n  * `ChunkByValue` Returns an `InputRange` that splits a range into sub-ranges\n    where `cb(a) == c(b)`.\n  * `Each` - Calls a callback with each value of a range.\n  * `Exhaust` - Steps through every element of a range until it's empty.\n    Similar to `Each` with an empty callback function, only `Front()` will never\n    be called for the range.\n  * `Filter` - Filter any `InputRange` with a callback.\n  * `FilterB` - Filter a `BidirectionalRange`, producing a range that can be\n    advanced in both directions. Less efficient for moving forwards, as it\n    requires priming the range in both directions.\n  * `Group` - Yields pairs of `(value, size)` counting how many values are equal\n    in each group according to `cb(a, b)`.\n  * `GroupComparable` - `Group` where `a == b` for any comparable value.\n  * `Joiner` - Joins ranges with a `separator` `ForwardRange` between ranges.\n  * `JoinerSS` - A special variation to join a slice of slices into a\n    `ForwardRange`.\n  * `JoinStrings` - A convenience function for creating a `string` from a\n    `ForwardRange` of `string` values with a `separator` `string`.\n  * `Map` - Map elements in any `InputRange` with a callback. The result of\n    calling the callback is not stored, so use `Cache` when generating ranges\n    with `Map`.\n  * `Permutations` - Given a slice of values, produce a `ForwardRange` of\n    all permutations of the given slice.\n  * `ReduceNoSeed` - Eagerly reduces a range to a single value without a seed\n    value. Panics when a range is empty.\n  * `SplitWhen` - Splits a range where `cb(a, b) == true` for adjacent elements.\n  * `Splitter` - Splits forward ranges with a `separator` `ForwardRange` between\n    ranges where `cb(a, b) == true`\n  * `SplitterSS` - A special variation to split a slice with a slice.\n  * `SplitterComparable` - `Splitter` where `a == b`.\n  * `SplitString` - A convenience function for splitting a `string` with\n    a `string.\n  * `Uniq` - Yields unique adjacent elements where `cb(a, b) == true`.\n  * `UniqComparable` - `Uniq` where `a == b`.\n* `mutation`\n  * `Copy` - Copies all values from an `InputRange` into an `OutputRange`.\n  * `Fill` - Assigns a value to all locations in a range of pointers.\n  * `FillPattern` - Assigns a pattern of values from a `ForwardRange` to all\n    locations in a range of pointers.\n  * `StripLeft` - Removes elements where `cb(a) == true` from the front of a\n    range.\n  * `StripLeftComparable` - `StripLeft` where `a == value`, given a provided\n    value.\n  * `StripRight` - Removes elements where `cb(a) == true` from the back of a\n    range.\n  * `StripRightComparable` - Removes elements where `a == value` from the back\n    of a range.\n  * `Strip` - Removes elements where `cb(a) == true` from the front and back of\n    a range.\n  * `StripComparable` - Removes elements where `a == value` from the front and\n    back of a range.\n* `searching`\n  * `All` - Checks if all elements in an `InputRange` satisfy a callback.\n  * `Any` - Checks if any elements in an `InputRange` satisfy a callback.\n  * `CanFind` - `Find`, but simply returns `true` if anything can be found.\n  * `CanFindComparable` - `FindComparable` but simply returns `true` if anything\n    can be found.\n  * `Length` - Returns the number of elements in an `InputRange` in `O(n)` time.\n  * `Count` - Returns the number of elements in an `InputRange` where the\n    callback returns `true`\n  * `CountUntil` - Returns the number of elements in an `InputRange` until the\n    callback returns `true`.\n  * `CommonPrefix` - Returns a `FowardRange` over the common prefix of two\n    ranges. The first range must be a `ForwardRange`.\n  * `DropWhile` - Advances a range while a callback returns `true`.\n  * `Find` - Advances a range until `cb(x, needle)` returns `true`, comparing\n    elements with a `needle`.\n  * `FindComparable` - Advances a range until `x == needle`.\n  * `FindEqual` - Advances a range until `cb(a, b)` returns `true` for every\n    element of a `needle` range.\n  * `FindEqualComparable` - Advances a range until `a == b` is satisfied for\n    all elements of a `needle` range.\n  * `FindAdjacent` - Advances a range until `cb(a, b)` returns `true` for two\n    adjacent elements in a `ForwardRange`.\n  * `FindAdjacentComparable` - Advances a range until `a == b` is satisfied for\n    two adjacent elements in a `ForwardRange`.\n  * `FindAmong` - Advances a range until `cb(a, b)` returns `true` for any\n    element of a `needle` range.\n  * `FindAmongComparable` - Advances a range until `a == b` is satisfied for any\n    element of a `needle` range.\n  * `SkipOver` - Skips over elements in a `haystack` `ForwardRange` if the range\n    starts with a `needle` range, according to `cb(a, b) == true`.\n  * `StartsWith` - Checks if one `InputRange` starts with another one, through a\n    callback.\n  * `TakeWhile` - Advances a range until the callback returns `true`.\n* `setops`\n  * `CartesianProduct` - Computes the `Cartesian` product of a series of forward\n    ranges.\n\n## Implementing New Ranges\n\nNew Ranges can be implemented by creating a struct with pointer receivers for\nall of the methods needed to satisfy a type of range. For example:\n\n```go\n// A pointer to this is an InputRange[T] because of the implementation below.\ntype newRangeType[T any] struct {\n  // Add whatever data you need.\n}\n\nfunc (r *newRangeType[T]) Empty() bool { /* ... */ }\nfunc (r *newRangeType[T]) Front() T    { /* ... */ }\nfunc (r *newRangeType[T]) PopFront()   { /* ... */ }\n\nfunc MakeNewRangeType[T any](/* ... */) InputRange[T] {\n  return \u0026newRangeType{/* ... */}\n}\n```\n\nIt's important to return pointers to your structs so ranges can be freely\npassed around as a reference type.\n\n## Performance\n\nWherever possible, algorithms will attempt to minimize allocations. The `Len()`\nof any `RandomAccessRange` objects at runtime, or the `len()` of slices may be\nused to determine how much memory to allocate, or to optimize algorithms to\navoid unnecessary operations.\n\nGo's compiler is capable of inlining many range function calls, which will\nreduce overhead. Ranges are likely to be stored in the garbage-collected heap.\nThe performance of using ranges will therefore be hard to predict. You should\nbenchmark and debug your code, and add optimizations where appropriate.\nSignificant improvements easily obtained are never premature.\n\n## Limitations\n\n### No function overloading for generic types\n\nIt's not possible to implement a `Filter` function that returns\n`ForwardRange[T]` if given `ForwardRange[T]` and returns `InputRange[T]` if\ngiven `InputRange[T]`.\n\nThe only available alternative in Go is to write multiple function names for\neach range type, such as `FilterF` for `ForwardRange[T]` and `Filter`\nfor `InputRange[T]`.\n\n### No covariant return types in Go\n\nWe need `SaveB` and `SaveR` methods because there are no covariant return types\nin Go. This means a user can be led to choosing a suboptimal save function.\nThere's no way around this in Go. In D, saving a range automatically carries\nacross the more specific details of the range type, such as `RandomAccessRange`\n`save` method returning a `RandomAccessRange`.\n\n### No tuple types and variadic generics\n\nVariadic generic types are difficult to implement correctly, and Go does not\nhave generic tuple types to represent any combination of values. This means that\njust as in classic Java generic interfaces, functions that take different\ncombinations of types must be redundantly defined up to some maximum number of\narguments in a programmer's imagination of how many arguments someone will call\na function with. For example:\n\n```go\nfunc Args0()[] { }\nfunc Args1[V1 any](v1 V1) { }\nfunc Args2[V1 any, V2](v1 V1, v2 V2) { }\nfunc Args3[V1 any, V2 any, V3 any](v1 V1, v2 V2, v3 V3) { }\nfunc Args3[V1 any, V2 any, V3 any, V4 any](v1 V1, v2 V2, v3 V3, v4 V4) { }\n```\n\nIt's not possible to write something like the following for all of the above:\n\n```go\nfunc Args[V any...](v V...) { }\n```\n\nBecause of this, multiple variations of the `Zip` function are required.\n\n## Advantages of Go\n\n### Exceptions must be an explicit part of the type of ranges\n\nGo does not have exceptions outside of `panic`. Ranges that can yield errors at\nany point have to explicitly include them in the type of result returned by\n`Front()`, so all ranges should be considered exception safe as long as the\ncontract of calling `Empty()` before `Front()` or `PopFront()` is not broken.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdense-analysis%2Franges","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdense-analysis%2Franges","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdense-analysis%2Franges/lists"}