Skip to content

Commit 05fb6f3

Browse files
authored
builtin: Implement min/max builtin function (#48)
This CL implementation is not 100% accurate since, keyfunc and default value parameter is not supported. We can support it later CL.
1 parent 95617b7 commit 05fb6f3

File tree

3 files changed

+195
-4
lines changed

3 files changed

+195
-4
lines changed

builtin/builtin.go

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ func init() {
4747
// py.MustNewMethod("iter", builtin_iter, 0, iter_doc),
4848
py.MustNewMethod("len", builtin_len, 0, len_doc),
4949
py.MustNewMethod("locals", py.InternalMethodLocals, 0, locals_doc),
50-
// py.MustNewMethod("max", builtin_max, 0, max_doc),
51-
// py.MustNewMethod("min", builtin_min, 0, min_doc),
50+
py.MustNewMethod("max", builtin_max, 0, max_doc),
51+
py.MustNewMethod("min", builtin_min, 0, min_doc),
5252
py.MustNewMethod("next", builtin_next, 0, next_doc),
5353
py.MustNewMethod("open", builtin_open, 0, open_doc),
5454
// py.MustNewMethod("oct", builtin_oct, 0, oct_doc),
@@ -772,6 +772,135 @@ func builtin_len(self, v py.Object) (py.Object, error) {
772772
return py.Len(v)
773773
}
774774

775+
const max_doc = `
776+
max(iterable, *[, default=obj, key=func]) -> value
777+
max(arg1, arg2, *args, *[, key=func]) -> value
778+
779+
With a single iterable argument, return its biggest item. The
780+
default keyword-only argument specifies an object to return if
781+
the provided iterable is empty.
782+
With two or more arguments, return the largest argument.`
783+
784+
func builtin_max(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
785+
return min_max(args, kwargs, "max")
786+
}
787+
788+
const min_doc = `
789+
min(iterable, *[, default=obj, key=func]) -> value
790+
min(arg1, arg2, *args, *[, key=func]) -> value
791+
792+
With a single iterable argument, return its smallest item. The
793+
default keyword-only argument specifies an object to return if
794+
the provided iterable is empty.
795+
With two or more arguments, return the smallest argument.`
796+
797+
func builtin_min(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) {
798+
return min_max(args, kwargs, "min")
799+
}
800+
801+
func min_max(args py.Tuple, kwargs py.StringDict, name string) (py.Object, error) {
802+
kwlist := []string{"key", "default"}
803+
positional := len(args)
804+
var format string
805+
var values py.Object
806+
var cmp func(a py.Object, b py.Object) (py.Object, error)
807+
if name == "min" {
808+
format = "|$OO:min"
809+
cmp = py.Le
810+
} else if name == "max" {
811+
format = "|$OO:max"
812+
cmp = py.Ge
813+
}
814+
var defaultValue py.Object
815+
var keyFunc py.Object
816+
var maxVal, maxItem py.Object
817+
var kf *py.Function
818+
819+
if positional > 1 {
820+
values = args
821+
} else {
822+
err := py.UnpackTuple(args, nil, name, 1, 1, &values)
823+
if err != nil {
824+
return nil, err
825+
}
826+
}
827+
err := py.ParseTupleAndKeywords(nil, kwargs, format, kwlist, &keyFunc, &defaultValue)
828+
if err != nil {
829+
return nil, err
830+
}
831+
if keyFunc == py.None {
832+
keyFunc = nil
833+
}
834+
if keyFunc != nil {
835+
var ok bool
836+
kf, ok = keyFunc.(*py.Function)
837+
if !ok {
838+
return nil, py.ExceptionNewf(py.TypeError, "'%s' object is not callable", keyFunc.Type())
839+
}
840+
}
841+
if defaultValue != nil {
842+
maxItem = defaultValue
843+
if keyFunc != nil {
844+
maxVal, err = py.Call(kf, py.Tuple{defaultValue}, nil)
845+
if err != nil {
846+
return nil, err
847+
}
848+
} else {
849+
maxVal = defaultValue
850+
}
851+
}
852+
iter, err := py.Iter(values)
853+
if err != nil {
854+
return nil, err
855+
}
856+
857+
for {
858+
item, err := py.Next(iter)
859+
if err != nil {
860+
if py.IsException(py.StopIteration, err) {
861+
break
862+
}
863+
return nil, err
864+
}
865+
if maxVal == nil {
866+
if keyFunc != nil {
867+
maxVal, err = py.Call(kf, py.Tuple{item}, nil)
868+
if err != nil {
869+
return nil, err
870+
}
871+
} else {
872+
maxVal = item
873+
}
874+
maxItem = item
875+
} else {
876+
var compareVal py.Object
877+
if keyFunc != nil {
878+
compareVal, err = py.Call(kf, py.Tuple{item}, nil)
879+
if err != nil {
880+
return nil, err
881+
}
882+
} else {
883+
compareVal = item
884+
}
885+
changed, err := cmp(compareVal, maxVal)
886+
if err != nil {
887+
return nil, err
888+
}
889+
if changed == py.True {
890+
maxVal = compareVal
891+
maxItem = item
892+
}
893+
}
894+
895+
}
896+
897+
if maxItem == nil {
898+
return nil, py.ExceptionNewf(py.ValueError, "%s() arg is an empty sequence", name)
899+
}
900+
901+
return maxItem, nil
902+
}
903+
775904
const chr_doc = `chr(i) -> Unicode character
776905
777906
Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.`

builtin/tests/builtin.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,63 @@ def fn(x):
7575
assert locals()["x"] == 1
7676
fn(1)
7777

78+
def func(p):
79+
return p[1]
80+
81+
doc="min"
82+
values = (1,2,3)
83+
v = min(values)
84+
assert v == 1
85+
v = min(4,5,6)
86+
assert v == 4
87+
v = min((), default=-1)
88+
assert v == -1
89+
v = min([], default=-1)
90+
assert v == -1
91+
v = min([], key=func, default=(1,3))
92+
assert v == (1,3)
93+
v = min([(1,3), (2,1)], key=func)
94+
assert v == (2,1)
95+
ok = False
96+
try:
97+
min([(1,3), (2,1)], key=3)
98+
except TypeError:
99+
ok = True
100+
assert ok, "TypeError not raised"
101+
ok = False
102+
try:
103+
min([])
104+
except ValueError:
105+
ok = True
106+
assert ok, "ValueError not raised"
107+
108+
doc="max"
109+
values = (1,2,3)
110+
v = max(values)
111+
assert v == 3
112+
v = max(4,5,6)
113+
assert v == 6
114+
v = max((), default=-1)
115+
assert v == -1
116+
v = max([], default=-1)
117+
assert v == -1
118+
v = max([], key=func, default=(1,3))
119+
assert v == (1,3)
120+
v = max([(1,3), (2,1)], key=func)
121+
assert v == (1,3)
122+
ok = False
123+
try:
124+
max([(1,3), (2,1)], key=3)
125+
except TypeError:
126+
ok = True
127+
assert ok, "TypeError not raised"
128+
ok = False
129+
try:
130+
max([])
131+
except ValueError:
132+
ok = True
133+
assert ok, "ValueError not raised"
134+
78135
doc="next no default"
79136
def gen():
80137
yield 1

py/args.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,11 +417,16 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist
417417
return ExceptionNewf(TypeError, "Internal error: supply the same number of results and kwlist")
418418
}
419419
min, max, name, ops := parseFormat(format)
420+
keywordOnly := false
420421
err := checkNumberOfArgs(name, len(args)+len(kwargs), len(results), min, max)
421422
if err != nil {
422423
return err
423424
}
424425

426+
if len(ops) > 0 && ops[0] == "$" {
427+
keywordOnly = true
428+
ops = ops[1:]
429+
}
425430
// Check all the kwargs are in kwlist
426431
// O(N^2) Slow but kwlist is usually short
427432
for kwargName := range kwargs {
@@ -442,10 +447,10 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist
442447
return ExceptionNewf(TypeError, "%s() got multiple values for argument '%s'", name, kw)
443448
}
444449
args = append(args, value)
450+
} else if keywordOnly {
451+
args = append(args, nil)
445452
}
446-
447453
}
448-
449454
for i, arg := range args {
450455
op := ops[i]
451456
result := results[i]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy