Я пытаюсь переопределить ноту среза питона на другом языке (php) и искать фрагмент (на любом языке или псевдокоде), который бы имитировал логику python. То есть, учитывая список и тройку (start, stop, step)
или ее часть, определите правильные значения или значения по умолчанию для всех параметров и верните срез в качестве нового списка.
Я попытался взглянуть на источник . Этот код намного превосходит мои навыки, но я не могу не согласиться с комментарием, говорящим:
/* 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 | python
или python test.py | php
python test.py | php
или python test.py | node
python test.py | node
Вот прямой порт кода 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)]
Это то, что я придумал (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
Кажется, работать – сообщите мне, что вы думаете
Надеюсь, поможет
Это решение, которое я придумал на 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(); }
Я написал порт PHP на основе кода C, оптимизированного для шаговых размеров -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; } }
Это основано на коде Python @ ecatmur, перенесенном снова на 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; }
Я не могу сказать, что в кодах нет ошибок, но прошло мимо вашей тестовой программы 🙂
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