- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
问题
有没有办法将函数参数声明为非严格的(通过 by-name )?
如果这不能直接实现:是否有任何辅助函数或装饰器可以帮助我实现类似的目标?
具体例子
这是一个可以用来试验的小玩具示例。
假设我想构建一个小型解析器组合器库,它可以处理以下带括号的算术表达式的经典语法(为简单起见,数字由单个文字值 1
替换):
num = "1"
factor = num
| "(" + expr + ")"
term = factor + "*" + term
| factor
expr = term + "+" + expr
| term
假设我将一个解析器组合器定义为一个对象,该对象具有一个方法parse
,该方法可以获取标记列表、当前位置,并抛出一个解析错误,或者返回结果和新位置。我可以很好地定义一个 ParserCombinator
基类,它提供 +
(串联)和 |
(替代)。然后我可以定义接受常量字符串的解析器组合器,并实现 +
和 |
:
# Two kinds of errors that can be thrown by a parser combinator
class UnexpectedEndOfInput(Exception): pass
class ParseError(Exception): pass
# Base class that provides methods for `+` and `|` syntax
class ParserCombinator:
def __add__(self, next):
return AddCombinator(self, next)
def __or__(self, other):
return OrCombinator(self, other)
# Literally taken string constants
class Lit(ParserCombinator):
def __init__(self, string):
self.string = string
def parse(self, tokens, pos):
if pos < len(tokens):
t = tokens[pos]
if t == self.string:
return t, (pos + 1)
else:
raise ParseError
else:
raise UnexpectedEndOfInput
def lit(str):
return Lit(str)
# Concatenation
class AddCombinator(ParserCombinator):
def __init__(self, first, second):
self.first = first
self.second = second
def parse(self, tokens, pos):
x, p1 = self.first.parse(tokens, pos)
y, p2 = self.second.parse(tokens, p1)
return (x, y), p2
# Alternative
class OrCombinator(ParserCombinator):
def __init__(self, first, second):
self.first = first
self.second = second
def parse(self, tokens, pos):
try:
return self.first.parse(tokens, pos)
except:
return self.second.parse(tokens, pos)
到目前为止,一切都很好。然而,因为语法的非终结符号是以相互递归的方式定义的,我不能急切地展开所有可能的解析器组合树,我必须使用解析器组合器的工厂,并且将它们包装成这样的东西:
# Wrapper that prevents immediate stack overflow
class LazyParserCombinator(ParserCombinator):
def __init__(self, parserFactory):
self.parserFactory = parserFactory
def parse(self, tokens, pos):
return self.parserFactory().parse(tokens, pos)
def p(parserFactory):
return LazyParserCombinator(parserFactory)
这确实允许我以非常接近 EBNF 的方式写下语法:
num = p(lambda: lit("1"))
factor = p(lambda: num | (lit("(") + expr + lit(")")))
term = p(lambda: (factor + lit("*") + term) | factor)
expr = p(lambda: (term + lit("+") + expr) | term)
它确实有效:
tokens = [str(x) for x in "1+(1+1)*(1+1+1)+1*(1+1)"]
print(expr.parse(tokens, 0))
但是,每一行中的 p(lambda: ...)
有点烦人。有没有一些惯用的方法来摆脱它?如果能够以某种方式“按名称”传递规则的整个 RHS,而不触发对无限相互递归的热切求值,那就太好了。
我尝试过的
我查看了核心语言中的可用内容:似乎只有if
、and
和or
可以“短路” ,如有错误请指正。
我已经尝试查看其他非玩具示例库是如何做到这一点的。
例如, funcparserlib使用明确的前向声明来避免相互递归(查看 forward_decl
和 value.define
github README.md 示例代码中的一部分)。
parsec.py
使用一些特殊的 @generate
装饰器并且似乎使用协程做一些类似单子(monad)解析的事情。这一切都很好,但我的目标是了解哪些选项我有关于可用的基本评估策略在 Python 中。
我还发现了类似 lazy_object_proxy.Proxy
的东西,但它似乎无助于以更简洁的方式实例化此类对象。
那么,有没有更好的方法来按名称传递参数并避免相互递归定义的值爆炸?
最佳答案
这是个好主意,但它不是 Python 语法所允许的:Python 表达式总是被严格求值(除了 if
block 和 and
和 或
短路表达式)。
特别是,问题是在这样的表达式中:
num = p(lit("1"))
p
函数参数总是以绑定(bind)到同一对象的新名称接收。评估 lit("1")
的对象没有命名任何东西(直到通过 p
的形式参数创建名称),所以那里没有名字可以绑定(bind)。相反,那里必须有一个对象,否则 p
根本无法接收值。
您可以做的是添加一个新对象来代替 lambda 来延迟对名称的评估。例如,像这样的东西:
class DeferredNamespace(object):
def __init__(self, namespace):
self.__namespace = namespace
def __getattr__(self, name):
return DeferredLookup(self.__namespace, name)
class DeferredLookup(object):
def __init__(self, namespace, name):
self.__namespace = namespace
self.__name = name
def __getattr__(self, name):
return getattr(getattr(self.__namespace, self.__name), name)
d = DeferredNamespace(locals())
num = p(d.lit("1"))
在这种情况下,d.lit
实际上不返回 lit
,它返回一个将使用 getattr 的
在实际使用时解析其成员。请注意,这会急切地捕获 DeferredLookup
对象(locals(), 'lit')locals()
,您可能不希望这样做;您可以调整它以使用 lambda,或者更好的是,无论如何只需在其他 namespace 中创建所有实体。
您仍然会在语法中遇到 d.
的缺点,这可能会或可能不会破坏交易,具体取决于您使用此 API 的目标。
关于python - Python 中的非严格按名称参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49674875/
我正在尝试做这样的事情:Name[i] = "Name"+ (i+1) 在 forloop 中,这样数组的值将是:Name[0] = Name1,Name[1] = Name2,Name[2] = N
我读了here,在GSP中我们可以这样写: ${params.action} 从GSP中,我们可以使用${params.action}作为参数调用Javascript函数(请参阅here)。 是否有其
我的问题:非常具体。我正在尝试想出解析以下文本的最简单方法: ^^domain=domain_value^^version=version_value^^account_type=account_ty
我创建了一条与此类似的路线: Router::connect("/backend/:controller/:action/*"); 现在我想将符合此模式的每个 Controller 路由重命名为类似
我在 Visual Studio 2013 项目中收到以下警告: SQL71502 - Procedure has an unresolved reference to object 最佳答案 这可以
任何人都可以指导我使用名称/值 .NET 集合或 .NET 名称/值字典以获得最佳性能吗?请问最好的方法是什么?我的应用程序是 ASP.NET、WCF/WF Web 应用程序。每个集合应该有 10 到
我在 Zend Framework 2 中有一个默认模块: namespace Application\Controller; use Zend\Mvc\Controller\AbstractActi
这是表格: 关于javascript - 在 javascript 中,这是一个有效的结构吗? : document. 名称.名称.值?,我们在Stack Overflow上找到一个类似的
HtmlHelper.ActionLink(htmlhelper,string linktext,string action) 如何找出正确的路线? 如果我有这个=> HtmlHelper.Actio
我需要一些有关如何将 Controller 定义传递给嵌套在 outer 指令中的 inner 指令的帮助。请参阅http://plnkr.co/edit/Om2vKdvEty9euGXJ5qan一个
请提出一个数据结构来表示内存中的记录列表。每条记录由以下部分组成: 用户名 积分 排名(基于积分)- 可选字段- 可以存储在记录中或可以动态计算 数据结构应该支持高效实现以下操作: Insert(re
错误 : 联合只能在具有兼容列类型的表上执行。 结构(层:字符串,skyward_number:字符串,skyward_points:字符串)<> 结构(skyward_number:字符串,层:字符
我想要一个包含可变数量函数的函数,但我希望在实际使用它们之前不要对它们求值。我可以使用 () => type 语法,但我更愿意使用 => type 语法,因为它似乎是为延迟评估而定制的。 当我尝试这样
我正在编写一个 elisp 函数,它将给定键永久绑定(bind)到当前主要模式的键盘映射中的给定命令。例如, (define-key python-mode-map [C-f1] 'pytho
卡在R中的错误上。 Error in names(x) <- value : 'names' attribute must be the same length as the ve
我有字符串,其中包含名称,有时在字符串中包含用户名,后跟日期时间戳: GN1RLWFH0546-2020-04-10-18-09-52-563945.txt JOHN-DOE-2020-04-10-1
有人知道为什么我会收到此错误吗?这显示将我的项目升级到新版本的Unity3d之后。 Error CS0103: The name `Array' does not exist in the curre
由于 Embarcadero 的 NNTP 服务器从昨天开始就停止响应,我想我可以在这里问:我使用非数据库感知网格,我需要循环遍历数据集以提取列数、它们的名称、数量行数以及每行中每个字段的值。 我知道
在构建Android应用程序的子项目中,我试图根据根build.gradle中的变量设置版本代码/名称。 子项目build.gradle: apply plugin: 'com.android.app
示例用例: 我有一个带有属性“myProperty”的对象,具有 getter 和 setter(自 EcmaScript 5 起支持“Property Getters 和 Setters”:http
我是一名优秀的程序员,十分优秀!