Der "läuft gegen"-Operator in C++
#include <iostream>
int main(int argc, char **argv)
{
int x = 10;
// x goes to 0
while (x --> 0)
std::cout << x << std::endl;
}
#include <iostream>
int main(int argc, char **argv)
{
int x = 10;
// x goes to 0
while (x --> 0)
std::cout << x << std::endl;
}
class Spam(object):
def __init__(self, value):
self.value = value
@property
def spam(self):
return self.value
@spam.setter
def spam(self, value):
self.value = value
spam = Spam(42)
print spam.spam
spam.spam = 23
print spam.spam
Manche Syntaxfehler können in CPython wegoptimiert werden:
>>> if 0:
... yield
...
>>> if 1:
... yield
File "<input>", line 2
SyntaxError: 'yield' outside function (<input>, line 2)
CPython unterstützt ja bekanntermaßen keine TCO von sich aus, weshalb es zahlreiche mehr oder weniger schöne Hacks gibt, die das CPython beibringen. Aber einen wirklich beeindruckenden habe ich eben in #python auf Freenode gesehen, geschrieben von habnabit. Und zwar wird der Bytecode geändert, CALL_FUNCTION wird zu JUMP_ABSOLUTE geändert.
Nachtrag: Das müsste eigentlich recht zuverlässig funktionieren, unter verschiedenen CPython-Versionen. Für das CALL_FUNCTION müssen die Argumente eh auf dem Stack liegen. Diese werden jetzt einfach mit STORE_FAST in die Namen geschrieben, die die Funktion entgegen nimmt, und dann wird wieder zum Anfang der Funktion gesprungen.
import inspect, pprint, types, dis, struct, opcode, array
short = struct.Struct('<H')
class Label(object):
pass
class Code(object):
@classmethod
def from_code(cls, code_obj):
self = cls()
self.code_obj = code_obj
self.names = list(code_obj.co_names)
self.varnames = list(code_obj.co_varnames)
self.consts = list(code_obj.co_consts)
ret = []
line_starts = dict(dis.findlinestarts(code_obj))
code = code_obj.co_code
labels = dict((addr, Label()) for addr in dis.findlabels(code))
i, l = 0, len(code)
extended_arg = 0
while i < l:
op = ord(code[i])
if i in labels:
ret.append(('MARK_LABEL', labels[i]))
if i in line_starts:
ret.append(('MARK_LINENO', line_starts[i]))
i += 1
if op >= opcode.HAVE_ARGUMENT:
arg, = short.unpack(code[i:i + 2])
arg += extended_arg
extended_arg = 0
i += 2
if op == opcode.EXTENDED_ARG:
extended_arg = arg << 16
continue
elif op in opcode.hasjabs:
arg = labels[arg]
elif op in opcode.hasjrel:
arg = labels[i + arg]
else:
arg = None
ret.append((opcode.opname[op], arg))
self.code = ret
return self
def to_code(self):
code_obj = self.code_obj
co_code = array.array('B')
co_lnotab = array.array('B')
label_pos = {}
jumps = []
lastlineno = code_obj.co_firstlineno
lastlinepos = 0
for op, arg in self.code:
if op == 'MARK_LABEL':
label_pos[arg] = len(co_code)
elif op == 'MARK_LINENO':
incr_lineno = arg - lastlineno
incr_pos = len(co_code) - lastlinepos
lastlineno = arg
lastlinepos = len(co_code)
if incr_lineno == 0 and incr_pos == 0:
co_lnotab.append(0)
co_lnotab.append(0)
else:
while incr_pos > 255:
co_lnotab.append(255)
co_lnotab.append(0)
incr_pos -= 255
while incr_lineno > 255:
co_lnotab.append(incr_pos)
co_lnotab.append(255)
incr_pos = 0
incr_lineno -= 255
if incr_pos or incr_lineno:
co_lnotab.append(incr_pos)
co_lnotab.append(incr_lineno)
elif arg is not None:
op = opcode.opmap[op]
if op in opcode.hasjabs or op in opcode.hasjrel:
jumps.append((len(co_code), arg))
arg = 0
if arg > 0xffff:
co_code.extend((opcode.EXTENDED_ARG,
(arg >> 16) & 0xff, (arg >> 24) & 0xff))
co_code.extend((op,
arg & 0xff, (arg >> 8) & 0xff))
else:
co_code.append(opcode.opmap[op])
for pos, label in jumps:
jump = label_pos[label]
if co_code[pos] in opcode.hasjrel:
jump -= pos + 3
assert jump <= 0xffff
co_code[pos + 1] = jump & 0xff
co_code[pos + 2] = (jump >> 8) & 0xff
return types.CodeType(code_obj.co_argcount, code_obj.co_nlocals,
code_obj.co_stacksize, code_obj.co_flags, co_code.tostring(),
tuple(self.consts), tuple(self.names), tuple(self.varnames),
code_obj.co_filename, code_obj.co_name, code_obj.co_firstlineno,
co_lnotab.tostring(), code_obj.co_freevars, code_obj.co_cellvars)
def const_idx(self, val):
try:
return self.consts.index(val)
except ValueError:
self.consts.append(val)
return len(self.consts) - 1
def tail_call(func):
code = Code.from_code(func.func_code)
func_name = func.__name__
if func_name in code.varnames:
raise SyntaxError('"%s" was found as a local variable in the function' %
func_name)
try:
name_idx = code.names.index(func_name)
except IndexError:
raise SyntaxError('"%s" not found in function\'s global names' %
func_name)
last_idx = 0
func_start = Label()
code.code.insert(0, ('MARK_LABEL', func_start))
while True:
try:
lglobal_idx = code.code.index(('LOAD_GLOBAL', name_idx), last_idx)
except ValueError:
break
if code.code[lglobal_idx - 1][0] != 'MARK_LINENO':
last_idx = lglobal_idx + 1
continue
try:
return_idx = code.code.index(('RETURN_VALUE', None), lglobal_idx)
except ValueError:
raise SyntaxError('"return" not found in function after "%s"' %
func_name)
if (return_idx != len(code.code) - 1
and code.code[return_idx + 1][0] != 'MARK_LINENO'):
last_idx = return_idx + 1
continue
if code.code[return_idx - 1][0] in ('CALL_FUNCTION_VAR',
'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW'):
raise SyntaxError('calling with *a and/or **kw is unsupported')
if code.code[return_idx - 1][0] != 'CALL_FUNCTION':
last_idx = return_idx + 1
continue
if code.code[return_idx - 1][1] & 0xff00:
raise SyntaxError('calling with keyword arguments is unsupported')
arg_names, _, _, defaults = inspect.getargspec(func)
n_args = code.code[return_idx - 1][1]
if defaults is None:
defaults = ()
if n_args + len(defaults) < len(arg_names):
raise SyntaxError('not enough arguments provided')
new_bytecode = []
if n_args < len(arg_names):
new_bytecode.extend(
('LOAD_CONST', code.const_idx(d))
for d in defaults[n_args - len(arg_names):])
new_bytecode.extend(
('STORE_FAST', code.varnames.index(arg))
for arg in reversed(arg_names))
new_bytecode.append(('JUMP_ABSOLUTE', func_start))
code.code[return_idx - 1:return_idx + 1] = new_bytecode
del code.code[lglobal_idx]
func.func_code = code.to_code()
return func
def factorial(n, acc=1):
if n <= 0:
return acc
return factorial(n - 1, n * acc)
dis.dis(factorial)
factorial = tail_call(factorial)
print
dis.dis(factorial)
print factorial(10000)
Der Code vom erl_eval-Modul von Erlang/OTP, der fun-Ausdrücke implementiert (kann man in lib/stdlib/src/erl_eval.erl finden):
%% This is a really ugly hack! F = case length(element(3,hd(Cs))) of 0 -> fun () -> eval_fun(Cs, [], En, Lf, Ef) end; 1 -> fun (A) -> eval_fun(Cs, [A], En, Lf, Ef) end; 2 -> fun (A,B) -> eval_fun(Cs, [A,B], En, Lf, Ef) end; 3 -> fun (A,B,C) -> eval_fun(Cs, [A,B,C], En, Lf, Ef) end; 4 -> fun (A,B,C,D) -> eval_fun(Cs, [A,B,C,D], En, Lf, Ef) end; 5 -> fun (A,B,C,D,E) -> eval_fun(Cs, [A,B,C,D,E], En, Lf, Ef) end; 6 -> fun (A,B,C,D,E,F) -> eval_fun(Cs, [A,B,C,D,E,F], En, Lf, Ef) end; 7 -> fun (A,B,C,D,E,F,G) -> eval_fun(Cs, [A,B,C,D,E,F,G], En, Lf, Ef) end; 8 -> fun (A,B,C,D,E,F,G,H) -> eval_fun(Cs, [A,B,C,D,E,F,G,H], En, Lf, Ef) end; 9 -> fun (A,B,C,D,E,F,G,H,I) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I], En, Lf, Ef) end; 10 -> fun (A,B,C,D,E,F,G,H,I,J) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J], En, Lf, Ef) end; 11 -> fun (A,B,C,D,E,F,G,H,I,J,K) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K], En, Lf, Ef) end; 12 -> fun (A,B,C,D,E,F,G,H,I,J,K,L) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L], En, Lf, Ef) end; 13 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M], En, Lf, Ef) end; 14 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N], En, Lf, Ef) end; 15 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O], En, Lf, Ef) end; 16 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P], En, Lf, Ef) end; 17 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q], En, Lf, Ef) end; 18 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R], En, Lf, Ef) end; 19 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S], En, Lf, Ef) end; 20 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) -> eval_fun(Cs, [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], En, Lf, Ef) end; _Other -> erlang:raise(error, {'argument_limit',{'fun',Line,Cs}}, stacktrace())
Beg, borrow, steal, buy, fabricate or otherwise obtain a rubber duck (bathtub variety)
Place rubber duck on desk and inform it you are just going to go over some code with it, if that's all right.
Explain to the duck what you code is supposed to do, and then go into detail and explain things line by line
At some point you will tell the duck what you are doing next and then realise that that is not in fact what you are actually doing. The duck will sit there serenely, happy in the knowledge that it has helped you on your way.
Works every time. Actually, if you don't have a rubber duck you could at a pinch ask a fellow programmer or engineer to sit in.