From f3b9939aab4554753ce385d4aa3bf12f5b5a04d8 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 6 Jan 2019 16:11:21 +0900 Subject: [PATCH] builtin: Implement min/max builtin function This CL implementation is not 100% accurate since, keyfunc and default value parameter is not supported. We can support it later CL. --- builtin/builtin.go | 133 ++++++++++++++++++++++++++++++++++++++- builtin/tests/builtin.py | 57 +++++++++++++++++ py/args.go | 9 ++- 3 files changed, 195 insertions(+), 4 deletions(-) diff --git a/builtin/builtin.go b/builtin/builtin.go index 0aea4767..218f8744 100644 --- a/builtin/builtin.go +++ b/builtin/builtin.go @@ -47,8 +47,8 @@ func init() { // py.MustNewMethod("iter", builtin_iter, 0, iter_doc), py.MustNewMethod("len", builtin_len, 0, len_doc), py.MustNewMethod("locals", py.InternalMethodLocals, 0, locals_doc), - // py.MustNewMethod("max", builtin_max, 0, max_doc), - // py.MustNewMethod("min", builtin_min, 0, min_doc), + py.MustNewMethod("max", builtin_max, 0, max_doc), + py.MustNewMethod("min", builtin_min, 0, min_doc), py.MustNewMethod("next", builtin_next, 0, next_doc), py.MustNewMethod("open", builtin_open, 0, open_doc), // py.MustNewMethod("oct", builtin_oct, 0, oct_doc), @@ -772,6 +772,135 @@ func builtin_len(self, v py.Object) (py.Object, error) { return py.Len(v) } +const max_doc = ` +max(iterable, *[, default=obj, key=func]) -> value +max(arg1, arg2, *args, *[, key=func]) -> value + +With a single iterable argument, return its biggest item. The +default keyword-only argument specifies an object to return if +the provided iterable is empty. +With two or more arguments, return the largest argument.` + +func builtin_max(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) { + return min_max(args, kwargs, "max") +} + +const min_doc = ` +min(iterable, *[, default=obj, key=func]) -> value +min(arg1, arg2, *args, *[, key=func]) -> value + +With a single iterable argument, return its smallest item. The +default keyword-only argument specifies an object to return if +the provided iterable is empty. +With two or more arguments, return the smallest argument.` + +func builtin_min(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) { + return min_max(args, kwargs, "min") +} + +func min_max(args py.Tuple, kwargs py.StringDict, name string) (py.Object, error) { + kwlist := []string{"key", "default"} + positional := len(args) + var format string + var values py.Object + var cmp func(a py.Object, b py.Object) (py.Object, error) + if name == "min" { + format = "|$OO:min" + cmp = py.Le + } else if name == "max" { + format = "|$OO:max" + cmp = py.Ge + } + var defaultValue py.Object + var keyFunc py.Object + var maxVal, maxItem py.Object + var kf *py.Function + + if positional > 1 { + values = args + } else { + err := py.UnpackTuple(args, nil, name, 1, 1, &values) + if err != nil { + return nil, err + } + } + err := py.ParseTupleAndKeywords(nil, kwargs, format, kwlist, &keyFunc, &defaultValue) + if err != nil { + return nil, err + } + if keyFunc == py.None { + keyFunc = nil + } + if keyFunc != nil { + var ok bool + kf, ok = keyFunc.(*py.Function) + if !ok { + return nil, py.ExceptionNewf(py.TypeError, "'%s' object is not callable", keyFunc.Type()) + } + } + if defaultValue != nil { + maxItem = defaultValue + if keyFunc != nil { + maxVal, err = py.Call(kf, py.Tuple{defaultValue}, nil) + if err != nil { + return nil, err + } + } else { + maxVal = defaultValue + } + } + iter, err := py.Iter(values) + if err != nil { + return nil, err + } + + for { + item, err := py.Next(iter) + if err != nil { + if py.IsException(py.StopIteration, err) { + break + } + return nil, err + } + if maxVal == nil { + if keyFunc != nil { + maxVal, err = py.Call(kf, py.Tuple{item}, nil) + if err != nil { + return nil, err + } + } else { + maxVal = item + } + maxItem = item + } else { + var compareVal py.Object + if keyFunc != nil { + compareVal, err = py.Call(kf, py.Tuple{item}, nil) + if err != nil { + return nil, err + } + } else { + compareVal = item + } + changed, err := cmp(compareVal, maxVal) + if err != nil { + return nil, err + } + if changed == py.True { + maxVal = compareVal + maxItem = item + } + } + + } + + if maxItem == nil { + return nil, py.ExceptionNewf(py.ValueError, "%s() arg is an empty sequence", name) + } + + return maxItem, nil +} + const chr_doc = `chr(i) -> Unicode character Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.` diff --git a/builtin/tests/builtin.py b/builtin/tests/builtin.py index d7140d68..4bb01106 100644 --- a/builtin/tests/builtin.py +++ b/builtin/tests/builtin.py @@ -75,6 +75,63 @@ def fn(x): assert locals()["x"] == 1 fn(1) +def func(p): + return p[1] + +doc="min" +values = (1,2,3) +v = min(values) +assert v == 1 +v = min(4,5,6) +assert v == 4 +v = min((), default=-1) +assert v == -1 +v = min([], default=-1) +assert v == -1 +v = min([], key=func, default=(1,3)) +assert v == (1,3) +v = min([(1,3), (2,1)], key=func) +assert v == (2,1) +ok = False +try: + min([(1,3), (2,1)], key=3) +except TypeError: + ok = True +assert ok, "TypeError not raised" +ok = False +try: + min([]) +except ValueError: + ok = True +assert ok, "ValueError not raised" + +doc="max" +values = (1,2,3) +v = max(values) +assert v == 3 +v = max(4,5,6) +assert v == 6 +v = max((), default=-1) +assert v == -1 +v = max([], default=-1) +assert v == -1 +v = max([], key=func, default=(1,3)) +assert v == (1,3) +v = max([(1,3), (2,1)], key=func) +assert v == (1,3) +ok = False +try: + max([(1,3), (2,1)], key=3) +except TypeError: + ok = True +assert ok, "TypeError not raised" +ok = False +try: + max([]) +except ValueError: + ok = True +assert ok, "ValueError not raised" + doc="next no default" def gen(): yield 1 diff --git a/py/args.go b/py/args.go index dcdfc392..f38fdfde 100644 --- a/py/args.go +++ b/py/args.go @@ -417,11 +417,16 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist return ExceptionNewf(TypeError, "Internal error: supply the same number of results and kwlist") } min, max, name, ops := parseFormat(format) + keywordOnly := false err := checkNumberOfArgs(name, len(args)+len(kwargs), len(results), min, max) if err != nil { return err } + if len(ops) > 0 && ops[0] == "$" { + keywordOnly = true + ops = ops[1:] + } // Check all the kwargs are in kwlist // O(N^2) Slow but kwlist is usually short for kwargName := range kwargs { @@ -442,10 +447,10 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist return ExceptionNewf(TypeError, "%s() got multiple values for argument '%s'", name, kw) } args = append(args, value) + } else if keywordOnly { + args = append(args, nil) } - } - for i, arg := range args { op := ops[i] result := results[i] 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