我正在尝试用另一种语言(php)重新实现python slice notation并寻找一个模仿python逻辑的片段(任何语言或伪代码)。也就是说,给定列表和三(start, stop, step)
或其中的一部分,确定所有参数的正确值或默认值,并将切片作为新列表返回。
我试着调查the source。该代码远远超出我的技能,但我不禁同意评论说:
/* this is harder to get right than you might think */
此外,如果已经完成了类似的事情,我们将非常感谢指针。
这是我的测试平台(确保您的代码在发布前通过):
#place your code below
code = """
def mySlice(L, start=None, stop=None, step=None):
or
<?php function mySlice($L, $start=NULL, $stop=NULL, $step=NULL) ...
or
function mySlice(L, start, stop, step) ...
"""
import itertools
L = [0,1,2,3,4,5,6,7,8,9]
if code.strip().startswith('<?php'):
mode = 'php'
if code.strip().startswith('def'):
mode = 'python'
if code.strip().startswith('function'):
mode = 'js'
if mode == 'php':
var, none = '$L', 'NULL'
print code, '\n'
print '$L=array(%s);' % ','.join(str(x) for x in L)
print "function _c($s,$a,$e){if($a!==$e)echo $s,' should be [',implode(',',$e),'] got [',implode(',',$a),']',PHP_EOL;}"
if mode == 'python':
var, none = 'L', 'None'
print code, '\n'
print 'L=%r' % L
print "def _c(s,a,e):\n\tif a!=e:\n\t\tprint s,'should be',e,'got',a"
if mode == 'js':
var, none = 'L', 'undefined'
print code, '\n'
print 'L=%r' % L
print "function _c(s,a,e){if(a.join()!==e.join())console.log(s+' should be ['+e.join()+'] got ['+a.join()+']');}"
print
n = len(L) + 3
start = range(-n, n) + [None, 100, -100]
stop = range(-n, n) + [None, 100, -100]
step = range(-n, n) + [100, -100]
for q in itertools.product(start, stop, step):
if not q[2]: q = q[:-1]
actual = 'mySlice(%s,%s)' % (var, ','.join(none if x is None else str(x) for x in q))
slice_ = 'L[%s]' % ':'.join('' if x is None else str(x) for x in q)
expect = eval(slice_)
if mode == 'php':
expect = 'array(%s)' % ','.join(str(x) for x in expect)
print "_c(%r,%s,%s);" % (slice_, actual, expect)
if mode == 'python':
print "_c(%r,%s,%s);" % (slice_, actual, expect)
if mode == 'js':
print "_c(%r,%s,%s);" % (slice_, actual, expect)
如何使用它:
test.py
)"""
s python test.py | python
或python test.py | php
或python test.py | node
答案 0 :(得分:17)
这是C代码的直接端口:
def adjust_endpoint(length, endpoint, step):
if endpoint < 0:
endpoint += length
if endpoint < 0:
endpoint = -1 if step < 0 else 0
elif endpoint >= length:
endpoint = length - 1 if step < 0 else length
return endpoint
def adjust_slice(length, start, stop, step):
if step is None:
step = 1
elif step == 0:
raise ValueError("step cannot be 0")
if start is None:
start = length - 1 if step < 0 else 0
else:
start = adjust_endpoint(length, start, step)
if stop is None:
stop = -1 if step < 0 else length
else:
stop = adjust_endpoint(length, stop, step)
return start, stop, step
def slice_indices(length, start, stop, step):
start, stop, step = adjust_slice(length, start, stop, step)
i = start
while (i > stop) if step < 0 else (i < stop):
yield i
i += step
def mySlice(L, start=None, stop=None, step=None):
return [L[i] for i in slice_indices(len(L), start, stop, step)]
答案 1 :(得分:2)
这就是我提出的(python)
def mySlice(L, start=None, stop=None, step=None):
answer = []
if not start:
start = 0
if start < 0:
start += len(L)
if not stop:
stop = len(L)
if stop < 0:
stop += len(L)
if not step:
step = 1
if stop == start or (stop<=start and step>0) or (stop>=start and step<0):
return []
i = start
while i != stop:
try:
answer.append(L[i])
i += step
except:
break
return answer
似乎工作 - 让我知道你的想法
希望有所帮助
答案 2 :(得分:2)
这是我在C#.NET中提出的解决方案,可能不是最漂亮的,但它确实有效。
private object[] Slice(object[] list, int start = 0, int stop = 0, int step = 0)
{
List<object> result = new List<object>();
if (step == 0) step = 1;
if (start < 0)
{
for (int i = list.Length + start; i < list.Length - (list.Length + start); i++)
{
result.Add(list[i]);
}
}
if (start >= 0 && stop == 0) stop = list.Length - (start >= 0 ? start : 0);
else if (start >= 0 && stop < 0) stop = list.Length + stop;
int loopStart = (start < 0 ? 0 : start);
int loopEnd = (start > 0 ? start + stop : stop);
if (step > 0)
{
for (int i = loopStart; i < loopEnd; i += step)
result.Add(list[i]);
}
else if (step < 0)
{
for (int i = loopEnd - 1; i >= loopStart; i += step)
result.Add(list[i]);
}
return result.ToArray();
}
答案 3 :(得分:1)
我编写了一个基于C代码的PHP端口,针对步长-1和1进行了优化:
function get_indices($length, $step, &$start, &$end, &$size)
{
if (is_null($start)) {
$start = $step < 0 ? $length - 1 : 0;
} else {
if ($start < 0) {
$start += $length;
if ($start < 0) {
$start = $step < 0 ? -1 : 0;
}
} elseif ($start >= $length) {
$start = $step < 0 ? $length - 1 : $length;
}
}
if (is_null($end)) {
$end = $step < 0 ? -1 : $length;
} else {
if ($end < 0) {
$end += $length;
if ($end < 0) {
$end = $step < 0 ? - 1 : 0;
}
} elseif ($end >= $length) {
$end = $step < 0 ? $length - 1 : $length;
}
}
if (($step < 0 && $end >= $start) || ($step > 0 && $start >= $end)) {
$size = 0;
} elseif ($step < 0) {
$size = ($end - $start + 1) / $step + 1;
} else {
$size = ($end - $start - 1) / $step + 1;
}
}
function mySlice($L, $start = NULL, $end = NULL, $step = 1)
{
if (!$step) {
return false; // could throw exception too
}
$length = count($L);
get_indices($length, $step, $start, $end, $size);
// optimize default step
if ($step == 1) {
// apply native array_slice()
return array_slice($L, $start, $size);
} elseif ($step == -1) {
// negative step needs an array reversal first
// with range translation
return array_slice(array_reverse($L), $length - $start - 1, $size);
} else {
// standard fallback
$r = array();
for ($i = $start; $step < 0 ? $i > $end : $i < $end; $i += $step) {
$r[] = $L[$i];
}
return $r;
}
}
答案 4 :(得分:1)
这是基于@ ecatmur的Python代码再次移植到PHP。
<?php
function adjust_endpoint($length, $endpoint, $step) {
if ($endpoint < 0) {
$endpoint += $length;
if ($endpoint < 0) {
$endpoint = $step < 0 ? -1 : 0;
}
}
elseif ($endpoint >= $length) {
$endpoint = $step < 0 ? $length - 1 : $length;
}
return $endpoint;
}
function mySlice($L, $start = null, $stop = null, $step = null) {
$sliced = array();
$length = count($L);
// adjust_slice()
if ($step === null) {
$step = 1;
}
elseif ($step == 0) {
throw new Exception('step cannot be 0');
}
if ($start === null) {
$start = $step < 0 ? $length - 1 : 0;
}
else {
$start = adjust_endpoint($length, $start, $step);
}
if ($stop === null) {
$stop = $step < 0 ? -1 : $length;
}
else {
$stop = adjust_endpoint($length, $stop, $step);
}
// slice_indices()
$i = $start;
$result = array();
while ($step < 0 ? ($i > $stop) : ($i < $stop)) {
$sliced []= $L[$i];
$i += $step;
}
return $sliced;
}
答案 5 :(得分:0)
我不能说代码中没有错误,但它已超过您的测试程序:)
def mySlice(L, start=None, stop=None, step=None):
ret = []
le = len(L)
if step is None: step = 1
if step > 0: #this situation might be easier
if start is None:
start = 0
else:
if start < 0: start += le
if start < 0: start = 0
if start > le: start = le
if stop is None:
stop = le
else:
if stop < 0: stop += le
if stop < 0: stop = 0
if stop > le: stop = le
else:
if start is None:
start = le-1
else:
if start < 0: start += le
if start < 0: start = -1
if start >= le: start = le-1
if stop is None:
stop = -1 #stop is not 0 because we need L[0]
else:
if stop < 0: stop += le
if stop < 0: stop = -1
if stop >= le: stop = le
#(stop-start)*step>0 to make sure 2 things:
#1: step != 0
#2: iteration will end
while start != stop and (stop-start)*step > 0 and start >=0 and start < le:
ret.append( L[start] )
start += step
return ret