Source File
builtin_fn_container.go
Belonging Package
src.elv.sh/pkg/eval
package evalimport ()// Sequence, list and maps.func () {addBuiltinFns(map[string]interface{}{"ns": nsFn,"make-map": makeMap,"range": rangeFn,"repeat": repeat,"assoc": assoc,"dissoc": dissoc,"all": all,"one": one,"has-key": hasKey,"has-value": hasValue,"take": take,"drop": drop,"count": count,"keys": keys,"order": order,})}//elvdoc:fn ns//// ```elvish// ns $map// ```//// Constructs a namespace from `$map`, using the keys as variable names and the// values as their values. Examples://// ```elvish-transcript// ~> n = (ns [&name=value])// ~> put $n[name]// ▶ value// ~> n: = (ns [&name=value])// ~> put $n:name// ▶ value// ```func ( hashmap.Map) (*Ns, error) {:= make(NsBuilder)for := .Iterator(); .HasElem(); .Next() {, := .Elem(), := .(string)if ! {return nil, errs.BadValue{What: `key of argument of "ns"`,Valid: "string", Actual: vals.Kind()}}[] = vars.FromInit()}return .Ns(), nil}//elvdoc:fn make-map//// ```elvish// make-map $input?// ```//// Outputs a map from an input consisting of containers with two elements. The// first element of each container is used as the key, and the second element is// used as the value.//// If the same key appears multiple times, the last value is used.//// Examples://// ```elvish-transcript// ~> make-map [[k v]]// ▶ [&k=v]// ~> make-map [[k v1] [k v2]]// ▶ [&k=v2]// ~> put [k1 v1] [k2 v2] | make-map// ▶ [&k1=v1 &k2=v2]// ~> put aA bB | make-map// ▶ [&a=A &b=B]// ```func ( Inputs) (vals.Map, error) {:= vals.EmptyMapvar error(func( interface{}) {if != nil {return}if !vals.CanIterate() {= errs.BadValue{What: "input to make-map", Valid: "iterable", Actual: vals.Kind()}return}if := vals.Len(); != 2 {= errs.BadValue{What: "input to make-map", Valid: "iterable with 2 elements",Actual: fmt.Sprintf("%v with %v elements", vals.Kind(), )}return}, := vals.Collect()if != nil {=return}if len() != 2 {= fmt.Errorf("internal bug: collected %v values", len())return}= .Assoc([0], [1])})return ,}//elvdoc:fn range//// ```elvish// range &step=1 $low? $high// ```//// Output `$low`, `$low` + `$step`, ..., proceeding as long as smaller than// `$high` or until overflow. If not given, `$low` defaults to 0. The `$step`// must be positive.//// This command is [exactness-preserving](#exactness-preserving).//// Examples://// ```elvish-transcript// ~> range 4// ▶ 0// ▶ 1// ▶ 2// ▶ 3// ~> range 1 6 &step=2// ▶ 1// ▶ 3// ▶ 5// ```//// When using floating-point numbers, beware that numerical errors can result in// an incorrect number of outputs://// ```elvish-transcript// ~> range 0.9 &step=0.3// ▶ (num 0.0)// ▶ (num 0.3)// ▶ (num 0.6)// ▶ (num 0.8999999999999999)// ```//// Avoid this problem by using exact rationals://// ```elvish-transcript// ~> range 9/10 &step=3/10// ▶ (num 0)// ▶ (num 3/10)// ▶ (num 3/5)// ```//// Etymology:// [Python](https://docs.python.org/3/library/functions.html#func-range).type rangeOpts struct{ Step vals.Num }func ( *rangeOpts) () { .Step = 1 }func ( *Frame, rangeOpts, ...vals.Num) error {var []vals.Numswitch len() {case 1:= []vals.Num{0, [0], .Step}case 2:= []vals.Num{[0], [1], .Step}default:return ErrArgs}switch step := .Step.(type) {case int:if <= 0 {return errs.BadValue{What: "step", Valid: "positive", Actual: strconv.Itoa()}}case *big.Int:if .Sign() <= 0 {return errs.BadValue{What: "step", Valid: "positive", Actual: .String()}}case *big.Rat:if .Sign() <= 0 {return errs.BadValue{What: "step", Valid: "positive", Actual: .String()}}case float64:if <= 0 {return errs.BadValue{What: "step", Valid: "positive", Actual: vals.ToString()}}}:= vals.UnifyNums(, vals.Int):= .OutputChan()switch nums := .(type) {case []int:, , := [0], [1], [2]for := ; < ; += {<- vals.FromGo()if + <= {// Overflowbreak}}case []*big.Int:, , := [0], [1], [2]:= &big.Int{}for .Set(); .Cmp() < 0; {<- vals.FromGo():= &big.Int{}.Add(, )=}case []*big.Rat:, , := [0], [1], [2]:= &big.Rat{}for .Set(); .Cmp() < 0; {<- vals.FromGo():= &big.Rat{}.Add(, )=}case []float64:, , := [0], [1], [2]for := ; < ; += {<- vals.FromGo()if + <= {// Overflowbreak}}default:panic("unreachable")}return nil}//elvdoc:fn repeat//// ```elvish// repeat $n $value// ```//// Output `$value` for `$n` times. Example://// ```elvish-transcript// ~> repeat 0 lorem// ~> repeat 4 NAN// ▶ NAN// ▶ NAN// ▶ NAN// ▶ NAN// ```//// Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).func ( *Frame, int, interface{}) {:= .OutputChan()for := 0; < ; ++ {<-}}//elvdoc:fn assoc//// ```elvish// assoc $container $k $v// ```//// Output a slightly modified version of `$container`, such that its value at `$k`// is `$v`. Applies to both lists and to maps.//// When `$container` is a list, `$k` may be a negative index. However, slice is not// yet supported.//// ```elvish-transcript// ~> assoc [foo bar quux] 0 lorem// ▶ [lorem bar quux]// ~> assoc [foo bar quux] -1 ipsum// ▶ [foo bar ipsum]// ~> assoc [&k=v] k v2// ▶ [&k=v2]// ~> assoc [&k=v] k2 v2// ▶ [&k2=v2 &k=v]// ```//// Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).//// @cf dissocfunc (, , interface{}) (interface{}, error) {return vals.Assoc(, , )}var errCannotDissoc = errors.New("cannot dissoc")//elvdoc:fn dissoc//// ```elvish// dissoc $map $k// ```//// Output a slightly modified version of `$map`, with the key `$k` removed. If// `$map` does not contain `$k` as a key, the same map is returned.//// ```elvish-transcript// ~> dissoc [&foo=bar &lorem=ipsum] foo// ▶ [&lorem=ipsum]// ~> dissoc [&foo=bar &lorem=ipsum] k// ▶ [&lorem=ipsum &foo=bar]// ```//// @cf assocfunc (, interface{}) (interface{}, error) {:= vals.Dissoc(, )if == nil {return nil, errCannotDissoc}return , nil}//elvdoc:fn all//// ```elvish// all $input-list?// ```//// Passes inputs to the output as is. Byte inputs into values, one per line.//// This is an identity function for commands with value outputs: `a | all` is// equivalent to `a` if it only outputs values.//// This function is useful for turning inputs into arguments, like://// ```elvish-transcript// ~> use str// ~> put 'lorem,ipsum' | str:split , (all)// ▶ lorem// ▶ ipsum// ```//// Or capturing all inputs in a variable://// ```elvish-transcript// ~> x = [(all)]// foo// bar// (Press ^D)// ~> put $x// ▶ [foo bar]// ```//// When given a list, it outputs all elements of the list://// ```elvish-transcript// ~> all [foo bar]// ▶ foo// ▶ bar// ```//// @cf onefunc ( *Frame, Inputs) {:= .OutputChan()(func( interface{}) { <- })}//elvdoc:fn one//// ```elvish// one $input-list?// ```//// Passes inputs to outputs, if there is only a single one. Otherwise raises an// exception.//// This function can be used in a similar way to [`all`](#all), but is a better// choice when you expect that there is exactly one output://// @cf allfunc ( *Frame, Inputs) error {var interface{}:= 0(func( interface{}) {if == 0 {=}++})if == 1 {.OutputChan() <-return nil}return fmt.Errorf("expect a single value, got %d", )}//elvdoc:fn take//// ```elvish// take $n $input-list?// ```//// Retain the first `$n` input elements. If `$n` is larger than the number of input// elements, the entire input is retained. Examples://// ```elvish-transcript// ~> take 3 [a b c d e]// ▶ a// ▶ b// ▶ c// ~> use str// ~> str:split ' ' 'how are you?' | take 1// ▶ how// ~> range 2 | take 10// ▶ 0// ▶ 1// ```//// Etymology: Haskell.func ( *Frame, int, Inputs) {:= .OutputChan():= 0(func( interface{}) {if < {<-}++})}//elvdoc:fn drop//// ```elvish// drop $n $input-list?// ```//// Drop the first `$n` elements of the input. If `$n` is larger than the number of// input elements, the entire input is dropped.//// Example://// ```elvish-transcript// ~> drop 2 [a b c d e]// ▶ c// ▶ d// ▶ e// ~> use str// ~> str:split ' ' 'how are you?' | drop 1// ▶ are// ▶ 'you?'// ~> range 2 | drop 10// ```//// Etymology: Haskell.//// @cf takefunc ( *Frame, int, Inputs) {:= .OutputChan():= 0(func( interface{}) {if >= {<-}++})}//elvdoc:fn has-value//// ```elvish// has-value $container $value// ```//// Determine whether `$value` is a value in `$container`.//// Examples, maps://// ```elvish-transcript// ~> has-value [&k1=v1 &k2=v2] v1// ▶ $true// ~> has-value [&k1=v1 &k2=v2] k1// ▶ $false// ```//// Examples, lists://// ```elvish-transcript// ~> has-value [v1 v2] v1// ▶ $true// ~> has-value [v1 v2] k1// ▶ $false// ```//// Examples, strings://// ```elvish-transcript// ~> has-value ab b// ▶ $true// ~> has-value ab c// ▶ $false// ```func (, interface{}) (bool, error) {switch container := .(type) {case hashmap.Map:for := .Iterator(); .HasElem(); .Next() {, := .Elem()if vals.Equal(, ) {return true, nil}}return false, nildefault:var bool:= vals.Iterate(, func( interface{}) bool {= ( == )return !})return ,}}//elvdoc:fn has-key//// ```elvish// has-key $container $key// ```//// Determine whether `$key` is a key in `$container`. A key could be a map key or// an index on a list or string. This includes a range of indexes.//// Examples, maps://// ```elvish-transcript// ~> has-key [&k1=v1 &k2=v2] k1// ▶ $true// ~> has-key [&k1=v1 &k2=v2] v1// ▶ $false// ```//// Examples, lists://// ```elvish-transcript// ~> has-key [v1 v2] 0// ▶ $true// ~> has-key [v1 v2] 1// ▶ $true// ~> has-key [v1 v2] 2// ▶ $false// ~> has-key [v1 v2] 0:2// ▶ $true// ~> has-key [v1 v2] 0:3// ▶ $false// ```//// Examples, strings://// ```elvish-transcript// ~> has-key ab 0// ▶ $true// ~> has-key ab 1// ▶ $true// ~> has-key ab 2// ▶ $false// ~> has-key ab 0:2// ▶ $true// ~> has-key ab 0:3// ▶ $false// ```func (, interface{}) bool {return vals.HasKey(, )}//elvdoc:fn count//// ```elvish// count $input-list?// ```//// Count the number of inputs.//// Examples://// ```elvish-transcript// ~> count lorem # count bytes in a string// ▶ 5// ~> count [lorem ipsum]// ▶ 2// ~> range 100 | count// ▶ 100// ~> seq 100 | count// ▶ 100// ```// The count implementation uses a custom varargs based implementation rather// than the more common `Inputs` API (see pkg/eval/go_fn.go) because this// allows the implementation to be O(1) for the common cases rather than O(n).func ( *Frame, ...interface{}) (int, error) {var intswitch := len(); {case 0:// Count inputs..IterateInputs(func(interface{}) {++})case 1:// Get length of argument.:= [0]if := vals.Len(); >= 0 {=} else {:= vals.Iterate(, func(interface{}) bool {++return true})if != nil {return 0, fmt.Errorf("cannot get length of a %s", vals.Kind())}}default:// The error matches what would be returned if the `Inputs` API was// used. See GoFn.Call().return 0, errs.ArityMismatch{What: "arguments here", ValidLow: 0, ValidHigh: 1, Actual: }}return , nil}//elvdoc:fn keys//// ```elvish// keys $map// ```//// Put all keys of `$map` on the structured stdout.//// Example://// ```elvish-transcript// ~> keys [&a=foo &b=bar &c=baz]// ▶ a// ▶ c// ▶ b// ```//// Note that there is no guaranteed order for the keys of a map.func ( *Frame, interface{}) error {:= .OutputChan()return vals.IterateKeys(, func( interface{}) bool {<-return true})}//elvdoc:fn order//// ```elvish// order &reverse=$false $less-than=$nil $inputs?// ```//// Outputs the input values sorted in ascending order. The sort is guaranteed to// be [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).//// The `&reverse` option, if true, reverses the order of output.//// The `&less-than` option, if given, establishes the ordering of the elements.// Its value should be a function that takes two arguments and outputs a single// boolean indicating whether the first argument is less than the second// argument. If the function throws an exception, `order` rethrows the exception// without outputting any value.//// If `&less-than` has value `$nil` (the default if not set), the following// comparison algorithm is used://// - Numbers are compared numerically. For the sake of sorting, `NaN` is treated// as smaller than all other numbers.//// - Strings are compared lexicographically by bytes, which is equivalent to// comparing by codepoints under UTF-8.//// - Lists are compared lexicographically by elements, if the elements at the// same positions are comparable.//// If the ordering between two elements are not defined by the conditions above,// no value is outputted and an exception is thrown.//// Examples://// ```elvish-transcript// ~> put foo bar ipsum | order// ▶ bar// ▶ foo// ▶ ipsum// ~> order [(float64 10) (float64 1) (float64 5)]// ▶ (float64 1)// ▶ (float64 5)// ▶ (float64 10)// ~> order [[a b] [a] [b b] [a c]]// ▶ [a]// ▶ [a b]// ▶ [a c]// ▶ [b b]// ~> order &reverse [a c b]// ▶ c// ▶ b// ▶ a// ~> order &less-than=[a b]{ eq $a x } [l x o r x e x m]// ▶ x// ▶ x// ▶ x// ▶ l// ▶ o// ▶ r// ▶ e// ▶ m// ```//// Beware that strings that look like numbers are treated as strings, not// numbers. To sort strings as numbers, use an explicit `&less-than` option://// ```elvish-transcript// ~> order [5 1 10]// ▶ 1// ▶ 10// ▶ 5// ~> order &less-than=[a b]{ < $a $b } [5 1 10]// ▶ 1// ▶ 5// ▶ 10// ```type orderOptions struct {Reverse boolLessThan Callable}func ( *orderOptions) () {}// ErrUncomparable is raised by the order command when inputs contain// uncomparable values.var ErrUncomparable = errs.BadValue{What: `inputs to "order"`,Valid: "comparable values", Actual: "uncomparable values"}func ( *Frame, orderOptions, Inputs) error {var []interface{}(func( interface{}) { = append(, ) })var errorvar func(, int) boolif .LessThan != nil {= func(, int) bool {if != nil {return true}var []interface{}if .Reverse {= []interface{}{[], []}} else {= []interface{}{[], []}}, := .CaptureOutput(func( *Frame) error {return .LessThan.Call(, , NoOpts)})if != nil {=return true}if len() != 1 {= errs.BadValue{What: "output of the &less-than callback",Valid: "a single boolean",Actual: fmt.Sprintf("%d values", len())}return true}if , := [0].(bool); {return}= errs.BadValue{What: "output of the &less-than callback",Valid: "boolean", Actual: vals.Kind([0])}return true}} else {// Use default comparison implemented by compare.= func(, int) bool {if != nil {return true}:= compare([], [])if == uncomparable {= ErrUncomparablereturn true}if .Reverse {return == more}return == less}}sort.SliceStable(, )if != nil {return}for , := range {.OutputChan() <-}return nil}type ordering uint8const (less ordering = iotaequalmoreuncomparable)func (, interface{}) ordering {switch a := .(type) {case float64:if , := .(float64); {switch {case math.IsNaN():if math.IsNaN() {return equal}return lesscase math.IsNaN():return morecase == :return equalcase < :return lessdefault:// a > breturn more}}case string:if , := .(string); {switch {case == :return equalcase < :return lessdefault:// a > breturn more}}case vals.List:if , := .(vals.List); {:= .Iterator():= .Iterator()for .HasElem() && .HasElem() {:= (.Elem(), .Elem())if != equal {return}.Next().Next()}switch {case .Len() == .Len():return equalcase .Len() < .Len():return lessdefault:// a.Len() > b.Len()return more}}}return uncomparable}
The pages are generated with Golds v0.2.8-preview. (GOOS=darwin GOARCH=arm64)