Skip to content

Commit

Permalink
Handle default args on First and Last
Browse files Browse the repository at this point in the history
  • Loading branch information
rocky committed Sep 26, 2024
1 parent 7667677 commit 73b99f0
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 6 deletions.
49 changes: 44 additions & 5 deletions mathics/builtin/list/eol.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,20 +581,32 @@ class First(Builtin):
<dl>
<dt>'First[$expr$]'
<dd>returns the first element in $expr$.
<dt>'First[$expr$, $def$]'
<dd>returns the first element in $expr$ if it exists or $def$ otherwise.
</dl>
'First[$expr$]' is equivalent to '$expr$[[1]]'.
>> First[{a, b, c}]
= a
The first argument need not be a list:
>> First[a + b + c]
= a
>> First[x]
: Nonatomic expression expected at position 1 in First[x].
= First[x]
>> First[{}]
: {} has zero length and no first element.
= First[{}]
As before, the first argument is empty, but a default argument is given, \
so evaluate and return the second argument:
>> First[{}, 1+2]
= 3
"""

attributes = A_HOLD_REST | A_PROTECTED
Expand All @@ -605,19 +617,24 @@ class First(Builtin):
}
summary_text = "first element of a list or expression"

# FIXME: the code and the code for Last are similar and can be DRY'd
def eval(self, expr, evaluation: Evaluation, expression: Expression):
"First[expr__]"

if isinstance(expr, Atom):
evaluation.message("First", "normal", expression)
return
if len(expr.elements) == 0:
expr_len = len(expr.elements)
if expr_len == 0:
evaluation.message("First", "nofirst", expr)
return
if len(expr.elements) > 2 and expr.head is SymbolSequence:
evaluation.message("First", "argt", len(expr.elements))
if expr_len > 2 and expr.head is SymbolSequence:
evaluation.message("First", "argt", expr_len)
return

if expr_len == 2 and isinstance(expr.elements[0], ListExpression):
return expr.elements[1].evaluate(evaluation)

return expr.elements[0]


Expand Down Expand Up @@ -819,36 +836,58 @@ class Last(Builtin):
<dl>
<dt>'Last[$expr$]'
<dd>returns the last element in $expr$.
<dt>'Last[$expr$, $def$]'
<dd>returns the last element in $expr$ if it exists or $def$ otherwise.
</dl>
'Last[$expr$]' is equivalent to '$expr$[[-1]]'.
>> Last[{a, b, c}]
= c
The first argument need not be a list:
>> Last[a + b + c]
= c
>> Last[x]
: Nonatomic expression expected at position 1 in Last[x].
= Last[x]
>> Last[{}]
: {} has zero length and no last element.
= Last[{}]
As before, the first argument is empty, but a default argument is given, \
so evaluate and return the second argument:
>> Last[{}, 1+2]
= 3
"""

attributes = A_HOLD_REST | A_PROTECTED
messages = {
"argt": "Last called with `1` arguments; 1 or 2 arguments are expected.",
"normal": "Nonatomic expression expected at position 1 in `1`.",
"nolast": "`1` has zero length and no last element.",
}
summary_text = "last element of a list or expression"

# FIXME: the code and the code for First are similar and can be DRY'd
def eval(self, expr, evaluation: Evaluation, expression: Expression):
"Last[expr_]"
"Last[expr__]"

if isinstance(expr, Atom):
evaluation.message("Last", "normal", expression)
return
if len(expr.elements) == 0:
expr_len = len(expr.elements)
if expr_len == 0:
evaluation.message("Last", "nolast", expr)
return
if expr_len > 2 and expr.head is SymbolSequence:
evaluation.message("Last", "argt", expr_len)
return

if expr_len == 2 and isinstance(expr.elements[0], ListExpression):
return expr.elements[1].evaluate(evaluation)

return expr.elements[-1]

Expand Down
8 changes: 7 additions & 1 deletion test/builtin/list/test_eol.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,13 @@
("a = {2,3,4}; i = 1; a[[i]] = 0; a", None, "{0, 3, 4}", None),
## Negative step
("{1,2,3,4,5}[[3;;1;;-1]]", None, "{3, 2, 1}", None),
("{1, 2, 3, 4, 5}[[;; ;; -1]]", None, "{5, 4, 3, 2, 1}", "MMA bug"),
("ClearAll[a]", None, "Null", None),
(
"Last[a, b, c]",
("Last called with 3 arguments; 1 or 2 arguments are expected.",),
"Last[a, b, c]",
None,
),
("Range[11][[-3 ;; 2 ;; -2]]", None, "{9, 7, 5, 3}", None),
("Range[11][[-3 ;; -7 ;; -3]]", None, "{9, 6}", None),
("Range[11][[7 ;; -7;; -2]]", None, "{7, 5}", None),
Expand Down

0 comments on commit 73b99f0

Please sign in to comment.