- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我知道函数比较在 Python 3 中是如何工作的(只是比较内存中的地址),我明白为什么。
我也明白“真正的”比较(函数 f
和 g
在给定相同参数的情况下返回相同的结果,对于任何参数?)实际上是不可能的。
我正在寻找介于两者之间的东西。我希望比较适用于具有相同功能的最简单情况,并且可能适用于一些不那么琐碎的情况:
lambda x : x == lambda x : x # True
lambda x : 2 * x == lambda y : 2 * y # True
lambda x : 2 * x == lambda x : x * 2 # True or False is fine, but must be stable
lambda x : 2 * x == lambda x : x + x # True or False is fine, but must be stable
请注意,我有兴趣为匿名函数 ( lambda
) 解决这个问题,但不介意该解决方案是否也适用于命名函数。
这样做的动机是内部 blist
模块,最好验证两个 sortedset
实例在对它们执行联合等操作之前具有相同的排序功能。
命名函数不太有趣,因为当它们不相同时我可以假设它们是不同的。毕竟,假设有人在 key
中创建了两个带有命名函数的排序集。争论。如果他们希望这些实例为了集合操作而“兼容”,他们可能会使用相同的函数,而不是执行相同操作的两个单独的命名函数。
我只能想到三种方法。所有这些看起来都很难,所以任何想法都会受到赞赏。
比较字节码可能有用,但它依赖于实现可能会很烦人(因此在一个 Python 上工作的代码在另一个 Python 上会中断)。
比较标记化的源代码似乎是合理且可移植的。当然,它没有那么强大(因为相同的功能更有可能被拒绝)。
从一些符号计算教科书中借鉴的可靠启发式算法在理论上是最好的方法。对于我的目的来说,它可能看起来太重了,但它实际上可能很合适,因为 lambda 函数通常很小,所以它运行得很快。
编辑
一个更复杂的例子,基于@delnan 的评论:
# global variable
fields = ['id', 'name']
def my_function():
global fields
s1 = sortedset(key = lambda x : x[fields[0].lower()])
# some intervening code here
# ...
s2 = sortedset(key = lambda x : x[fields[0].lower()])
我会期待 s1
的关键功能吗?和 s2
评估为平等?
如果中间代码包含任何函数调用,fields
的值可能会被修改,导致 s1
的不同键功能和 s2
.由于我们显然不会通过控制流分析来解决这个问题,所以很明显,如果我们试图在运行时之前执行此评估,我们必须将这两个 lambda 函数评估为不同的。 (即使 fields
不是全局的,它也可能绑定(bind)了另一个名称,等等)这将严重削弱整个练习的实用性,因为很少有 lambda 函数不依赖于环境。
编辑 2:
我意识到比较运行时存在的函数对象非常重要。否则,所有依赖于外部作用域变量的函数都无法进行比较;大多数有用的函数都有这样的依赖性。在运行时考虑,所有具有相同签名的函数都可以以一种干净、合乎逻辑的方式进行比较,无论它们依赖什么,是否不纯等等。
因此,我不仅需要字节码,还需要创建函数对象时的全局状态(大概是 __globals__
)。然后我必须将外部作用域中的所有变量与 __globals__
中的值进行匹配.
最佳答案
编辑以检查外部状态是否会影响排序功能以及这两个功能是否等效。
我破解了 dis.dis
和 friend 们输出到一个全局文件类对象。然后,我删除了行号和规范化的变量名(不涉及常量)并比较了结果。
您可以清理它,以便 dis.dis
和 friend yield
输出行,这样您就不必捕获他们的输出。但这是使用 dis.dis
进行最小更改的功能比较的有效概念验证。
import types
from opcode import *
_have_code = (types.MethodType, types.FunctionType, types.CodeType,
types.ClassType, type)
def dis(x):
"""Disassemble classes, methods, functions, or code.
With no argument, disassemble the last traceback.
"""
if isinstance(x, types.InstanceType):
x = x.__class__
if hasattr(x, 'im_func'):
x = x.im_func
if hasattr(x, 'func_code'):
x = x.func_code
if hasattr(x, '__dict__'):
items = x.__dict__.items()
items.sort()
for name, x1 in items:
if isinstance(x1, _have_code):
print >> out, "Disassembly of %s:" % name
try:
dis(x1)
except TypeError, msg:
print >> out, "Sorry:", msg
print >> out
elif hasattr(x, 'co_code'):
disassemble(x)
elif isinstance(x, str):
disassemble_string(x)
else:
raise TypeError, \
"don't know how to disassemble %s objects" % \
type(x).__name__
def disassemble(co, lasti=-1):
"""Disassemble a code object."""
code = co.co_code
labels = findlabels(code)
linestarts = dict(findlinestarts(co))
n = len(code)
i = 0
extended_arg = 0
free = None
while i < n:
c = code[i]
op = ord(c)
if i in linestarts:
if i > 0:
print >> out
print >> out, "%3d" % linestarts[i],
else:
print >> out, ' ',
if i == lasti: print >> out, '-->',
else: print >> out, ' ',
if i in labels: print >> out, '>>',
else: print >> out, ' ',
print >> out, repr(i).rjust(4),
print >> out, opname[op].ljust(20),
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
extended_arg = 0
i = i+2
if op == EXTENDED_ARG:
extended_arg = oparg*65536L
print >> out, repr(oparg).rjust(5),
if op in hasconst:
print >> out, '(' + repr(co.co_consts[oparg]) + ')',
elif op in hasname:
print >> out, '(' + co.co_names[oparg] + ')',
elif op in hasjrel:
print >> out, '(to ' + repr(i + oparg) + ')',
elif op in haslocal:
print >> out, '(' + co.co_varnames[oparg] + ')',
elif op in hascompare:
print >> out, '(' + cmp_op[oparg] + ')',
elif op in hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
print >> out, '(' + free[oparg] + ')',
print >> out
def disassemble_string(code, lasti=-1, varnames=None, names=None,
constants=None):
labels = findlabels(code)
n = len(code)
i = 0
while i < n:
c = code[i]
op = ord(c)
if i == lasti: print >> out, '-->',
else: print >> out, ' ',
if i in labels: print >> out, '>>',
else: print >> out, ' ',
print >> out, repr(i).rjust(4),
print >> out, opname[op].ljust(15),
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i+1])*256
i = i+2
print >> out, repr(oparg).rjust(5),
if op in hasconst:
if constants:
print >> out, '(' + repr(constants[oparg]) + ')',
else:
print >> out, '(%d)'%oparg,
elif op in hasname:
if names is not None:
print >> out, '(' + names[oparg] + ')',
else:
print >> out, '(%d)'%oparg,
elif op in hasjrel:
print >> out, '(to ' + repr(i + oparg) + ')',
elif op in haslocal:
if varnames:
print >> out, '(' + varnames[oparg] + ')',
else:
print >> out, '(%d)' % oparg,
elif op in hascompare:
print >> out, '(' + cmp_op[oparg] + ')',
print >> out
def findlabels(code):
"""Detect all offsets in a byte code which are jump targets.
Return the list of offsets.
"""
labels = []
n = len(code)
i = 0
while i < n:
c = code[i]
op = ord(c)
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i+1])*256
i = i+2
label = -1
if op in hasjrel:
label = i+oparg
elif op in hasjabs:
label = oparg
if label >= 0:
if label not in labels:
labels.append(label)
return labels
def findlinestarts(code):
"""Find the offsets in a byte code which are start of lines in the source.
Generate pairs (offset, lineno) as described in Python/compile.c.
"""
byte_increments = [ord(c) for c in code.co_lnotab[0::2]]
line_increments = [ord(c) for c in code.co_lnotab[1::2]]
lastlineno = None
lineno = code.co_firstlineno
addr = 0
for byte_incr, line_incr in zip(byte_increments, line_increments):
if byte_incr:
if lineno != lastlineno:
yield (addr, lineno)
lastlineno = lineno
addr += byte_incr
lineno += line_incr
if lineno != lastlineno:
yield (addr, lineno)
class FakeFile(object):
def __init__(self):
self.store = []
def write(self, data):
self.store.append(data)
a = lambda x : x
b = lambda x : x # True
c = lambda x : 2 * x
d = lambda y : 2 * y # True
e = lambda x : 2 * x
f = lambda x : x * 2 # True or False is fine, but must be stable
g = lambda x : 2 * x
h = lambda x : x + x # True or False is fine, but must be stable
funcs = a, b, c, d, e, f, g, h
outs = []
for func in funcs:
out = FakeFile()
dis(func)
outs.append(out.store)
import ast
def outfilter(out):
for i in out:
if i.strip().isdigit():
continue
if '(' in i:
try:
ast.literal_eval(i)
except ValueError:
i = "(x)"
yield i
processed_outs = [(out, 'LOAD_GLOBAL' in out or 'LOAD_DECREF' in out)
for out in (''.join(outfilter(out)) for out in outs)]
for (out1, polluted1), (out2, polluted2) in zip(processed_outs[::2], processed_outs[1::2]):
print 'Bytecode Equivalent:', out1 == out2, '\nPolluted by state:', polluted1 or polluted2
输出为True
、True
、False
和False
,并且是稳定的。如果输出将取决于外部状态——全局状态或闭包,则“污染” bool 值为真。
关于python - 开发启发式方法来测试简单的匿名 Python 函数的等效性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9963155/
场景: Suppose I have a large pseudorandom graph complete with edge weights, but without any coordinate
我有一个巨大的人名列表,我必须在巨大的文本中进行搜索。 只有名称的一部分可以出现在文本中。并且可能存在拼写错误、打字错误或缩写。文本没有标记,因此我不知道文本中人名的开头位置。我不知道这个名字是否会出
我在尝试总结这些启发式算法的最坏情况比率时遇到了一些麻烦(这意味着它满足三角不等式)旅行商问题: 最近的邻居 最近的插入 最便宜的插入 最远插入 最近的邻居: Here它表示 NN 的 w-C 比率为
我正在为 2048 开发一个 AI。到目前为止它非常简单,我基本上是在尝试制作一个由递减方 block 组成的“蛇”,所以完美的游戏应该是这样的: ,虽然这和这个一样好: . 我的启发式方法是使用一个
我从 stdin 中读取了一个正整数 N,然后我试图确定 N 是否是素数。 我知道我可以将 N 除以所有正数直到 sqrt(N),但这很耗时,而且我的算法有时会给出误报,所以我正在寻找一种启发式方法来
我对高估/低估这两个术语感到困惑。我完全了解 A* 算法的工作原理,但我不确定高估或低估启发式算法的效果。 取直接鸟瞰线的平方是否高估?为什么它会使算法不正确?所有节点都使用相同的启发式。 直接鸟瞰线
我有一个问题,由一个有墙、目标和代理的方形迷宫组成。代理只能水平/垂直移动。在每一步,每个智能体从 1 个方格移动。 我必须实现 A* 算法来解决问题,但我很难找到一个很好的启发式算法来解决它。 每次
首先,我看到了这个答案,是的,它解释了 X-Y 启发式算法,但是示例板太简单了,我无法理解一般的启发式算法。 X-Y heuristic function for solving N-puzzle 有
我正在尝试为清晰 map 的吃 bean 人游戏想出一个又好又快的启发式方法。 我的启发式方法是尝试计算吃 bean 人到达 map 上每个有食物的点所需的最小可能距离。我当前的算法基本上是 Prim
我只是玩弄 Python 并发现了一件有趣的事情:我的计算机(i5,3 GHz)在尝试计算 10 ** 10 ** 10 几个小时后就停止运行了。我知道数学不是创建 Python 的目的,但我想知道是
我理解杀手启发式背后的想法以及它为什么有帮助。我正在努力解决的是如何在 Alpha-Beta 搜索例程中实现它。特别是如何保证只先尝试兄弟节点的杀手级 Action ?伪代码会有很大帮助。 最佳答案
我已经实现了 Clarke-Wright 启发法来解决 TSP(基于伪代码 here )。我已附上我在 Matlab 中的实现。然而,它对我来说不够快,并且需要 O(n2) 空间(因为成对距离)。我想
我是一名优秀的程序员,十分优秀!