3.7 lists
include lists
import lists as ...
3.7.1 The List Datatype
3.7.2 The list Constructor
Constructs a list out of the elts by chaining links, ending in a single empty.
check: [list: ] is empty [list: 1] is link(1, empty) [list: 1, 2] is link(1, link(2, empty)) end
Note: explicitly writing the trailing empty is both unnecessary and wrong; the constructor notation needs only the elements of the list.
3.7.3 List Methods
These methods are available on all lists (both link and empty instances). The examples show how to use the dot operator to access and call them on particular lists.
Returns the number of elements in the list.
check: empty.length() is 0 link("a", empty).length() is 1 end
Applies f to each element of the list from left to right, and constructs a new list out of the return values in the corresponding order.
check: [list: 1, 2].map(lam(n): n + 1 end) is [list: 2, 3] [list: 1, 2].map(num-tostring) is [list: "1", "2"] empty.map(lam(x): raise "This never happens!" end) is empty end
Applies f to each element of the list from left to right, and returns nothing. Because it returns nothing, use each instead of map when the function f is needed only for its side-effects.
check: var x = 1 [list: 1, 2].each(lam(n): x := x + n end) is nothing x is 4 end
Applies f to each element of list from left to right, constructing a new list out of the elements for which f returned true.
check: fun length-is-one(s :: String): string-length(s) == 1 end [list: "ab", "a", "", "c"].filter(length-is-one) is [list: "a", "c"] [list: empty, link(1, empty), empty].filter(is-link) is [list: link(1, empty)] end
Returns link(elt, rest).
check: empty.push("a") is link("a", empty) link("a", empty).push("b") is link("b", link("a", empty)) end
Produces a record containing two lists, consisting of the items before and the items at-or-after the splitting index of the current list. The index is 0-based, so splitting a list at index n will produce a prefix of length exactly n. Moreover, appending the two lists together will be equivalent to the original list.
check: one-four = link(1, link(2, link(3, link(4, empty)))) one-four.split-at(0) is { prefix: empty, suffix: one-four } one-four.split-at(4) is { prefix: one-four, suffix: empty } one-four.split-at(2) is { prefix: link(1, link(2, empty)), suffix: link(3, link(4, empty)) } one-four.split-at(-1) raises "Invalid index" one-four.split-at(5) raises "Index too large" for each(i from range(0, 4)): split = one-four.split-at(i) split.prefix.length() is i split.prefix.append(split.suffix) is one-four end end
Given a length n, returns a new list containing the first n items of the list.
check: [list: 1, 2, 3, 4, 5, 6].take(3) is [list: 1, 2, 3] [list: 1, 2, 3].take(6) raises "Index too large" [list: 1, 2, 3].take(-1) raises "Invalid index" end
Given a length n, returns a list containing all but the first n items of the list.
check: [list: 1, 2, 3, 4, 5, 6].drop(3) is [list: 4, 5, 6] end
check: [list: 1, 2, 3].get(0) is 1 [list: ].get(0) raises "too large" [list: 1, 2, 3].get(-1) raises "invalid argument" end
Returns a new list, with the same contents as the existing list, except the value at the specified index is replaced with the given value.
check: [list: 1, 2, 3].set(0, 5) is [list: 5, 2, 3] [list: ].set(0, 5) raises "too large" end
Computes f(last-elt, ... f(second-elt, f(first-elt, base))...). For empty, returns base. In other words, it uses f to combine base with each item in the list starting from the left.
check: fun combine(elt, acc): tostring(elt) + ", " + acc end [list: 3, 2, 1].foldl(combine, "END") is "1, 2, 3, END" empty.foldl(combine, "END") is "END" [list: 3, 2, 1].foldl(link, empty) is link(1, link(2, link(3, empty))) end
Computes f(first-elt, f(second-elt, ... f(last-elt, base))). For empty, returns base. In other words, it uses f to combine base with each item in the list starting from the right.
check: fun combine(elt, acc): tostring(elt) + ", " + acc end [list: 3, 2, 1].foldr(combine, "END") is "1, 2, 3, END" [list: 3, 2, 1].foldr(link, empty) is link(3, link(2, link(1, empty))) end
Returns true if the current list contains the given value, as compared by ==.
check: [list: 1, 2, 3].member(2) is true [list: 2, 4, 6].member(3) is false [list: ].member(empty) is false [list: ~1, ~2, ~3].member(~1) raises "Roughnums" end
Produces a new list with all the elements of the current list, followed by all the elements of the given list.
check: [list: 1, 2].append([list: 3, 4]) is [list: 1, 2, 3, 4] empty.append([list: 1, 2]) is [list: 1, 2] [list: 1, 2].append(empty) is [list: 1, 2] end
check: [list: 1, 2, 3].last() is 3 empty.last() raises "last of empty list" end
check: [list: 1, 2, 3].reverse() is [list: 3, 2, 1] empty.reverse() is empty end
check: [list: 1, 5, 3, 2, 4].sort() is [list: 1, 2, 3, 4, 5] [list: "aaaa", "B", "a"].sort() is [list: "B", "a", "aaaa"] [list: true, false].sort() raises "binop-error" end
check: fun true-after-false(x, y): y and not(x) end [list: true, false].sort-by(true-after-false, lam(x, y): x == y end) is [list: false, true] end
check: [list: 1, 2, 3].join-str(", ") is "1, 2, 3" [list: "a", true, ~5.3].join-str(" : ") is "a : true : ~5.3" empty.join-str("nothing at all") is "" end
3.7.4 List Functions
These functions are available on the lists module object. So, for example, if you used import lists as L, you would write L.fold to access fold below. The list module itself, along with many list functions, are available by default in Pyret. Check out the section on global identifiers to learn more.
Returns the length of the list.
examples: length([list: "a", "b", "c"]) is 3 length([list: ]) is 0 end
Given a list of numbers, returns the largest. Raises an error on empty lists.
examples: max([list: 1, 2, 3]) is 3 max([list: ]) raises "empty" max([list: ~1, 3, 7/2]) is 7/2 end
Given a list of numbers, returns the smallest. Raises an error on empty lists.
examples: min([list: 1, 2, 3]) is 1 min([list: ]) raises "empty" min([list: ~1, 3, -7/2]) is -7/2 end
Given a list of numbers x1, x2, ..., xn, returns x1 + x2 + ... + xn.
examples: sum([list: 1, 2, 3]) is 6 sum([list: ]) is 0 sum([list: ~1, 3]) is-roughly ~4 end
Given a list of numbers, returns their arithmetic mean. Raises an error on empty lists.
examples: mean([list: 1, 2, 6]) is 3 mean([list: ]) raises "empty" mean([list: ~1, 3]) is-roughly ~2 end
Given a list of numbers, returns their median (taking the mean of the two middle numbers if the list has even length). Raises an error on empty lists.
examples: median([list: 1, 2, 6]) is 2 median([list: ]) raises "empty" median([list: ~1, 3]) is-roughly ~2 end
Given a list of numbers, returns their standard deviation. Raises an error on empty lists.
examples: stdev([list: 3, 4, 5, 6, 7]) is%(within(0.01)) 1.41 stdev([list: 1, 1, 1, 1]) is-roughly ~0 stdev([list:]) raises "empty" end
Given a list, returns a new list containing only one copy of each element that is duplicated in the list. The last (latest in the list) copy is kept. Roughnums are not compared for equality, and so will always appear in the output list.
examples: distinct([list: 3, 1, 2, 2, 3, 2]) is [list: 1, 3, 2] distinct([list: ~1, ~1]) is-roughly [list: ~1, ~1] distinct([list: ~1, ~1, 1]) is-roughly [list: ~1, ~1, 1] distinct([list: ~1, ~1, 1, 1]) is-roughly [list: ~1, ~1, 1] distinct([list: ~1, ~2, ~3]) is-roughly [list: ~1, ~2, ~3] end
Returns the nth element of the given list, or raises an error if n is out of range
check: lists.get([list: 1, 2, 3], 0) is 1 lists.get([list: ], 0) raises "" end
Returns a new list with the same values as the given list but with the nth element set to the given value, or raises an error if n is out of range
check: set([list: 1, 2, 3], 0, 5) is [list: 5, 2, 3] set([list: 1, 2, 3], 5, 5) raises "" end
check: sort([list: 1, 5, 3, 2, 4]) is [list: 1, 2, 3, 4, 5] sort([list: "aaaa", "B", "a"]) is [list: "B", "a", "aaaa"] sort([list: true, false]) raises "binop-error" end
check: lists.sort-by([list: { name: "Bob", age: 22 }, { name: "Amy", age: 5 }, { name: "Bob", age: 17 }, { name: "Joan", age: 43 }, { name: "Alex", age: 3 }], lam(p1, p2): p1.age < p2.age end, lam(p1, p2): p1.age == p2.age end) is [list: { name: "Alex", age: 3 }, { name: "Amy", age: 5 }, { name: "Bob", age: 17 }, { name: "Bob", age: 22 }, { name: "Joan", age: 43 }] end
Creates a list of numbers, starting with start, ending with stop-1
check: range(0, 0) is [list: ] range(0, 1) is [list: 0] range(-5, 5) is [list: -5, -4, -3, -2, -1, 0, 1, 2, 3, 4] end
Creates a list with n copies of e
check: repeat(0, 10) is empty repeat(3, -1) is [list: -1, -1, -1] repeat(1, "foo") is link("foo", empty) end
Returns the subset of lst for which f(elem) is true
check: filter(lam(e): e > 5 end, [list: -1, 1]) is [list: ] filter(lam(e): e > 0 end, [list: -1, 1]) is [list: 1] end
Splits the list into two lists, one for which f(elem) is true, and one for which f(elem) is false
check: partition(lam(e): e > 0 end, [list: -1, 1]) is { is-true: [list: 1], is-false: [list: -1] } partition(lam(e): e > 5 end, [list: -1, 1]) is { is-true: [list: ], is-false: [list: -1, 1] } partition(lam(e): e < 5 end, [list: -1, 1]) is { is-true: [list: -1, 1], is-false: [list: ] } end
check: find(lam(elt): elt > 1 end, [list: 1, 2, 3]) is some(2) find(lam(elt): elt > 4 end, [list: 1, 2, 3]) is none find(lam(elt): true end, [list: "find-me", "miss-me"]) is some("find-me") find(lam(elt): true end, empty) is none find(lam(elt): false end, [list: "miss-me"]) is none find(lam(elt): false end, empty) is none end
Splits the list into two lists, one containing the first n elements, and the other containing the rest
check: let one-four = [list: 1, 2, 3, 4]: split-at(0, one-four) is { prefix: empty, suffix: one-four } split-at(4, one-four) is { prefix: one-four, suffix: empty } split-at(2, one-four) is { prefix: [list: 1, 2], suffix: [list: 3, 4] } split-at(-1, one-four) raises "Invalid index" split-at(5, one-four) raises "Index too large" end end
Returns the last element in lst. Raises an error if the list is empty.
check: last([list: 1, 3, 5]) is 5 last([list: 1]) is 1 last([list: ]) raises "last of empty list" end
Produce a new list with the elements of front followed by the elements of back.
check: append([list: 1, 2, 3], [list: 4, 5, 6]) is [list: 1, 2, 3, 4, 5, 6] append([list:], [list:]) is [list:] append([list: 1], [list: 2]) is [list: 1, 2] end
Note that it does not change either list:
check: l = [list: 1, 2, 3] append(l, [list: 4]) l is [list: 1, 2, 3, 4] # this test fails end
Returns true if f(elem) returns true for any elem of lst
check: any(lam(n): n > 1 end, [list: 1, 2, 3]) is true any(lam(n): n > 3 end, [list: 1, 2, 3]) is false any(lam(x): true end, empty) is false any(lam(x): false end, empty) is false end
Returns true if f(elem) returns true for all elems of lst
check: all(lam(n): n > 1 end, [list: 1, 2, 3]) is false all(lam(n): n <= 3 end, [list: 1, 2, 3]) is true all(lam(x): true end, empty) is true all(lam(x): false end, empty) is true end
Returns true if f(elem1, elem2) returns true for all corresponding elems of lst1 and list2. Returns true when either list is empty
all2(lam(n, m): n > m end, [list: 1, 2, 3], [list: 0, 1, 2]) is true all2(lam(n, m): (n + m) == 3 end, [list: 1, 2, 3], [list: 2, 1, 0]) is true all2(lam(n, m): n < m end, [list: 1, 2, 3], [list: 0, 1, 2]) is false all2(lam(_, _): true end, empty, empty) is true all2(lam(_, _): false end, empty, empty) is true
Returns a list made up of f(elem) for each elem in lst
map(lam(_): 2 end, [list: 1, 2, 3, 4]) is [list: 2, 2, 2, 2] map(lam(x): x + 1 end, [list: 1, 2, 3, 4]) is [list: 2, 3, 4, 5]
Returns a list made up of f(elem1, elem2) for each elem1 in l1, elem2 in l2
map2(lam(x, y): x or y end, [list: true, false], [list: false, false]) is [list: true, false]
Returns a list made up of f(e1, e2, e3) for each e1 in l1, e2 in l2, e3 in l3
Returns a list made up of f(e1, e2, e3, e4) for each e1 in l1, e2 in l2, e3 in l3, e4 in l4
Returns a list made up of f(n, e1), f(n+1, e2) .. for e1, e2 ... in lst
Like map, but also includes a numeric argument for the position in the list that is currently being mapped over.
check: map_n(lam(n, e): n end, 0, [list: "captain", "first mate"]) is [list: 0, 1] end
Returns a list made up of f(i, e1, e2) for each e1 in l1, e2 in l2, and i counting up from n
Like map_n, but for two-argument functions.
Returns a list made up of f(i, e1, e2, e3) for each e1 in l1, e2 in l2, e3 in l3, and i counting up from n
Returns a list made up of f(i, e1, e2, e3, e4) for each e1 in l1, e2 in l2, e3 in l3, e4 in l4, and i counting up from n
Calls f for each elem in lst, and returns nothing
check: let one-four = [list: 1, 2, 3, 4]: let var counter = 0: each(lam(n): counter := counter + n end, one-four) counter is 1 + 2 + 3 + 4 counter is 10 end let var counter = 1: each(lam(n): counter := counter * n end, one-four) counter is 1 * 2 * 3 * 4 counter is 24 end end end
Calls f on each pair of corresponding elements in l1 and l2, and returns nothing. Stops after the shortest list
Calls f on each triple of corresponding elements in l1, l2 and l3, and returns nothing. Stops after the shortest list
Calls f on each tuple of corresponding elements in l1, l2, l3 and l4, and returns nothing. Stops after the shortest list
Calls f(i, e) for each e in lst and with i counting up from num, and returns nothing
Like each, but also includes a numeric argument for the position in the list that is currently being visited.
Calls f(i, e1, e2) for each e1 in lst1, e2 in lst2 and with i counting up from num, and returns nothing
Calls f(i, e1, e2, e3) for each e1 in lst1, e2 in lst2, e3 in lst3 and with i counting up from num, and returns nothing
Calls f(i, e1, e2, e3, e4) for each e1 in lst1, e2 in lst2, e3 in lst3, e4 in lst4 and with i counting up from num, and returns nothing
- fold-while :: (
- f :: (a, b -> Either<a, a>),
- base :: a,
- lst :: List<b>
- )
- -> a
Takes a function that takes two arguments and returns an Either, and also a base value, and folds over the given list from the left as long as the function returns a left() value, and returns either the final value or the right() value
fold applies a procedure, f, to combine or "fold" the elements of a list into a single value.
f takes two arguments. The first is the result thus far, the second is the current element of this list. f is initially invoked with base, and the first item of each list, as there is no result thus far. Each element from left to right is then successively fed to f, and the result of the whole fold application is the result of the last application of f. If the list is empty, base is returned.
check: fold(lam(acc, cur): acc end, 1, [list: 1, 2, 3, 4]) is 1 fold(lam(acc, cur): cur end, 1, [list: 1, 2, 3, 4]) is 4 fold(lam(acc, cur): acc + cur end, 0, [list: 1, 2, 3, 4]) is 10 fold(lam(lst, elt): link(elt, lst) end, empty, [list: 1, 2, 3]) is [list: 3, 2, 1] end
Takes a function, an initial value and a list, and folds the function over the list from the left, starting with the initial value
Another name for fold.
Takes a function, an initial value and a list, and folds the function over the list from the right, starting with the initial value
check: foldr(lam(acc, cur): acc + cur end, 0, [list: 1, 2, 3, 4]) is 10 foldr(lam(lst, elt): link(elt, lst) end, empty, [list: 1, 2, 3]) is [list: 1, 2, 3] end
Takes a function, an initial value and two lists, and folds the function over the lists in parallel from the left, starting with the initial value and ending when either list is empty
Takes a function, an initial value and three lists, and folds the function over the lists in parallel from the left, starting with the initial value and ending when any list is empty
Takes a function, an initial value and four lists, and folds the function over the lists in parallel from the left, starting with the initial value and ending when any list is empty
Takes a function, an initial value and a list, and folds the function over the list from the left, starting with the initial value and passing along the index (starting with the given num)
Like fold, but takes a numeric argument for the position in the list that is currently being visited.
check: fold_n(lam(n, acc, _): n * acc end, 1, 1, [list: "a", "b", "c", "d"]) is 1 * 2 * 3 * 4 fold_n(lam(n, acc, cur): tostring(n) + " " + cur + ", " + acc end, 95, "and so forth...", repeat(5, "jugs o' grog in the hold")) is "99 jugs o' grog in the hold, 98 jugs o' grog in the hold, " + "97 jugs o' grog in the hold, 96 jugs o' grog in the hold, " + "95 jugs o' grog in the hold, and so forth..." fold_n(lam(n, acc, cur): ((num-modulo(n, 2) == 0) or cur) and acc end, 0, true, [list: false, true, false]) is true end
- member-with :: (
- lst :: List<a>,
- elt :: a,
- eq :: (a, a -> equality.EqualityResult)
- )
- -> Any
Returns a new list with all the elements of the original list in random order.
check "shuffle": l = [list: 1, 2, 3, 4] l-mixed = lists.shuffle(l) sets.list-to-set(l-mixed) is sets.list-to-set(l) l-mixed.length() is l.length() end