Pair
A Pair is a container for exactly two objects. Since Sidef is dynamically typed, its elements may be any type, including other Pairs.
This eliminates the need for a separate LinkedList class, as nested Pairs serve the same purpose.
If a Pair is created missing one or both elements, that element will be nil
.
The literal syntax for creating Pairs is the :
method on Objects, which is not an operator, and therefore cannot have a nil
left-side.
"paired":"strings" # -> Pair("paired", "strings")
Pair("paired", "strings") # -> ==/==
"a":nil # -> Pair("a", nil)
nil:"b" # -> error, methods cannot be called on undefined values
Pair(nil, "b") # -> Pair(nil, "b")
Note that the Pair literal syntax does not round-trip, i.e Pair.to_s
will never give the :
representation.
When using the Pair literal syntax with variable names, this overlaps with the syntax for Complex numbers and NamedParams.
var (a, b) = ("a", "b")
say a:b # prints "a:"
say a:b.is_a(Pair) # prints false
# it has type NamedParam
var (c, d) = (1, 2)
say c:d # prints 1+2i
say c:d.is_a(Pair) # prints false
say c:d.class # prints Complex
When the left-hand side is not a number, force evaluation of :
as a method with .
or \
:
say a.:b # prints Pair("a", "b")
say a\:b # ==/==
say (a : b) # ==/==
say Pair(a, b) # ==/==
say c.:d # prints 1+2i
say c\:d # ==/==
say (c : d) # ==/==
say Pair(c, d) # prints Pair(1, 2)
Note this still calls the :
method on numbers, converting its arguments to a Complex, so stick with the Pair()
constructor if the types are uncertain.
Indexing
Pair objects can be indexed directly, but also define the first
/ key
and second
/ value
methods:
var pair = Pair(1, 2)
say pair.first # prints 1
say pair.key # ==/==
say pair[0] # ==/==
say pair.second # prints 2
say pair.value # ==/==
say pair[1] # ==/==
say pair[2] # prints nil
say pair[4..10] # a range of nils
Like other iterable and indexable objects, accessing a nonexistent Pair element gives nil
.
Nesting
To use nested pairs like a Lisp-style linked list, the second
or value
methods can be chained, as long as the second element is always a Pair.
The :
method can be combined with the infix :
operator, which symbol-ifies its operand, to easily write chains and linked lists of Strings.
:a # -> "a", the named variable a
:a: :chain: :of: :string: :pairs # -> Pair("a", Pair("chain", Pair("of", Pair("string", "pairs"))))
:a::chain::of::string::pairs # -> ==/==
:a::chain::of::string::pairs: # -> Pair(String, NamedParam)
Including a trailing :
in such chains will cause the rest of the expression to be parsed as a NamedParam.
To make this a (old-style) linked list, we should be able to find the end, thus include a trailing nil
(or null
, or false
...). Then we can traverse it, destructively for brevity:
var list = :a: :linked: :list: :of: :pairs: :of: :strings: nil
var copy = list.dclone
# imperative style
loop {
print copy.key+" "
break if (! copy.value!)
}
# prints: a linked list of pairs of strings
# copy is now nil, the terminating element
# functional style
func traverse (list) {
print list.key+" "
__FUNC__(list) if list.value!
}
traverse(list.dclone)
# prints: a linked list of pairs of strings
The swap
method can be used to transpose the first and second elements:
(:a : :b) # -> Pair("a", "b")
(:a : :b).swap # -> Pair("b", "a")