LINUX.ORG.RU

Элегантный способ генерации последовательности для чтения массива порциями

 ,


0

1

Хочется сделать рефакторинг пис-оф-кода.

Нужно для массива длиной в M элементов получить в другом массиве пары чисел, позволяющие считать первый массив равными порциями «по частям».

См. код ниже. Здесь $m - это количество элементов в массиве. $c - это размер порции для считывания.

Я это делаю сейчас так:

use Data::Dumper;
my $m=90;
my $c=15;
my @seq=map { 
 my $t1=$_*$c;
 my $t2=$t1+$c-1;
 $t2>($m-1)?$t1==$m?():[$t1=>$m-1]:[$t1=>$t2]
} 0..int($m/$c);
print Dumper \@seq

На выходе получаю массив @seq:

$VAR1 = [
          [
            0,
            14
          ],
          [
            15,
            29
          ],
          [
            30,
            44
          ],
          [
            45,
            59
          ],
          [
            60,
            74
          ],
          [
            75,
            89
          ]
        ];

Внимание, вопрос: как сделать то же самое:

а) Быстрее

б) Экономичнее

в) Лаконичнее

?

Например, использование splice не отвечает требованиям «быстрее» и «экономичнее», хотя удовлетворяет «лаконичнее».

Предыдущая часть мучений на ту же тему: «Разделяй и властвуй»: как обрабатывать списки «порциями»

★★★★★

Последнее исправление: DRVTiny (всего исправлений: 1)

Вот чуть более красивый вариант, тут хотя бы нет тупняка с умножениями на ровном месте.

my $r=0;
map { my $l=$r; $r+=$s; $r>$n?$l==$n?():[$l=>$n-1]:[$l=>$r-1] } 0..int($n/$s)
DRVTiny ★★★★★
() автор топика

а) Я бы вообще не парился с генерацией списка, и работал напрямую с $m и $c. подозреваю, что эти интервалы всё равно пойдут в $a..$b

for (my $i = 0; $i < $m; $i += $c) {
  foreach my $j ($i .. ($i + $c)) {
    # do some shit
  }
}

Это может быть нужно только в одном случае - генерация постраничной навигации, но и там в половине случаев оперируешь только номером страницы.

б) форматирование у тебя поганое. пробелы экономишь?

anonymous
()
Ответ на: комментарий от anonymous

б) Экономлю :) Я вообще прижимистый весьма. А ещё «1» - это такое дискретно-детерминированно-обоснованное количество, с ним никогда не ошибёшься, ибо 1 - оно и а Африке 1.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

взять перл порт lodash, разбить на куски

anonymous
()
Ответ на: комментарий от anonymous

В итоге внутри всего этого зарыт тот же splice, да ещё плюс вызов функции на каждую порцию. Нафига? Речь же о том, как сделать максимально быстрый код, а не о том, как его сделать максимально программистки-замороченным. Можно ещё ООП сюда прибабахать, сделать красивую обёртку, а в итоге внутри всё равно будет splice. Это не мой путь, извините.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Кстати, если уж говорить об итераторе, то он мог бы и запоминать, где он остановился в прошлый раз и всё-таки делать срез массива, а не сплайсить

DRVTiny ★★★★★
() автор топика

наверное начать с apt-get install python ?

max_lapshin ★★★★★
()

Хотя нет, я не понял что тебе нужно.

Может быть это?

my @arr;
{
    my ($first, $last) = (0, $c - 1);
    for(; $last < $m; $first += $c, $last += $c) {
	push @arr, [$first, $last];
    }
    push(@arr, [$first, $m - 1])
	if $first < $m;
}
print Dumper(\@arr);
soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
  use List::MoreUtils::XS ':all';

  my @x = ('a' .. 'g');
  my $it = natatime 3, @x;
  while (my @vals = $it->())
  {
    print "@vals\n";
  }
pru-mike ★★
()

зачем создавать отдельную сущность «указатели» если можно сразу разбить оригинальный массив на части? или он может иметь «бесконечную» длину?

system-root ★★★★★
()

Перепиши на APL. Там, наверное, каких-нибудь три закорючки выйдет всего. Лаконичнее твой алгоритм получится только в ЯП без т@кого $инт@к$иче$кого мусора.

Virtuos86 ★★★★★
()
Ответ на: комментарий от anonymous

И многие python3 уже пользуются? Я что-то всё чаще программы на python 2.7 встречаю.

Вообще как только программы на python научатся не высирать стек-трейсы в ответ на любую нештатную ситуацию (я пользователь этих программ, и мне не хочется знать, какая там функция откуда вызывается, я хочу узнавать из сообщения об ошибке, что пошло не так) - возможно, я и заинтересуюсь пихтоном. А пока у меня создаётся впечатление, что это какой-то язык для школьнегов или в лучшем случае индусов, не способных проверить параметры функций на корректность и выдать вразумительное сообщение об ошибке.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Лол, если школьнеги не умеют разбирать стектрейсы, которые показывают, где возникла ошибка, то это не проблема языка. Если бы в Perl были бы нормальные исключения, то он высирал бы аналогичные портянки.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Лол, если школьнеги не умеют разбирать стектрейсы, которые показывают, где возникла ошибка

Только школьнеги считают, что пользователи должны читать их говно-стек-трейсы, а не посылать разработчиков на *** и лезть в инеты за ПО в том числе и на Perl, которое хотя бы диагностировать свои ошибки может нормально, в том числе и логи вести, а не только в консольку срать.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Тебе это в итоге нужно было то?

Элегантный способ генерации последовательности для чтения массива порциями (комментарий)

А то я так и не понял, чего ты хотел от своего скрипта.

Мой код вроде под описание

«Нужно для массива длиной в M элементов получить в другом массиве пары чисел, позволяющие считать первый массив равными порциями «по частям». См. код ниже. Здесь $m - это количество элементов в массиве. $c - это размер порции для считывания.»

подходит.

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от soomrack

Я ещё Try::Tiny активно юзаю для перехвата «внезапных» исключений.

Но большинство исключений просеиваются просто внимательным анализом переданных параметров, т.е. ещё до того, как тот или иной код начнёт работать с переданным ему мусором.

А ещё у Perl есть великолепный Taint-mode, позволяющий понять, какие переменные содержат некие значения из «внешнего мира» и могут быть небезопасны (поскольку содержат произвольную ересь).

Но когда такое «солидное» приложение на Python как vpoller высирает стек-трейс в качестве реакции на абсолютно банальную ситуацию «сконфигурирован неверный логин или пароль для авторизации vCenter'е» - у любого нормального пользователя возникает нездоровое желание плюнуть разработчику в лицо и спросить, назачем он «обрабатывает исключения» там, где можно было без проблем внятно и подробно сообщить об ошибке. Что, типа очень спешил разработчик, руки не дошли?

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от soomrack

Твой код не очень отвечает требованию в), он не вполне лаконичен.

А так да - относительно быстрый и экономичный.

Вот бы то же самое, но на map... :)

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

Taint-mode должен быть включен просто по-умолчанию всегда. И обязательно указан при работе с базами (там он отдельно в DBI врубается).

Python — отличный язык, как замена Паскалю при обучении. Его ограничения в плане стилистики написания кода — они очень полезны для выработки правильной культуры и навыков оформления кода. В целом python сейчас занимает место фортрана 80х (точнее связка python + с). И опять же, если на нем пишут миллионы людей, которые не программисты, а лингвисты, ученые, халявщики, полувебмастера и пр.пр.пр., то все его ограничения приводят к тому, что код остается читабелен. Это круто!

Но да, для серьезного программирования, для написания надежных систем, он слабоват. И выразительность слабая и пока еще библиотечный инструментарий мал. А уж про изоляцию переменных вообще вспоминать не хочется (ее просто нет, private переменных нет! ну и нафига тогда такие классы?)... А лямбда функции питона? Смех да и только....

Но как язык для масс — он отличный.

soomrack ★★★★★
()
Ответ на: комментарий от DRVTiny
my @arr2 = map{[($_-1)*$c,$_*$c-1]} 1..int($m/$c);
push @arr2, [int($m/$c)*$c, $m-1] if ($m%$c);

или однострочник:

my @arr2 = ( map{ [($_-1)*$c,$_*$c-1] } 1..int($m/$c), $m%$c ? [int($m/$c)*$c, $m-1] : () ) ;

Но все это хуже моего первого варианта и по понятности кода и по производительности.

soomrack ★★★★★
()
Ответ на: комментарий от DRVTiny

Только школьнеги^W DRVTiny считает, что ЯП должен быть ответственен за то, чтобы приложения на нем умело вести логи и диагностировать свои ошибки. Я правильно понимаю, что любой скрипт на Perl несет подобные фичи искаропки только потому, что он написан на Perl?

Virtuos86 ★★★★★
()
Ответ на: комментарий от soomrack

Но да, для серьезного программирования, для написания надежных систем, он слабоват.

Да. Но не из-за

И выразительность слабая и пока еще библиотечный инструментарий мал.

а по причине динамической типизации.

А уж про изоляцию переменных вообще вспоминать не хочется (ее просто нет, private переменных нет! ну и нафига тогда такие классы?)...

Очень свежий взгляд на ООП.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Я правильно понимаю, что любой скрипт на Perl несет подобные фичи искаропки только потому, что он написан на Perl?

Речь не о скриптах, а о приложениях. Или Python не готов для написания на нём приложений? Очень жаль. Perl уже лет 20 как, и написано на нём уже немало весьма.

Ну так вот, есть некая культура разработки на том или ином языке. Она конечно с языковыми средствами не связана напрямую, но... где вы видели PHP-код, умеющий что-либо корректно и подробно логировать? Или где вы видели Python код, который не высирает стек-трейсы, только потому что «может»?

Perl - это язык для эстетов, как я уже неоднократно говорил. На нём конечно можно писать как угодно, но хороший программист всё-таки старается писать на нём «умный» код, который и выглядит хорошо, и ведёт себя адекватно. И да, культура языка Perl не предполагает стек-трейсов: как минимум потому что даже штатный die просто выводит сообщение об ошибке, а не загаживает консоль мусором, фактически предлагающим отлаживать программу «даже» тогда (99% случаев), когда отлаживать там категорически нечего: произошла не ошибка внутри программы, а совершенно штатная ситуация типа «порт закрыт» или «пароль не подходит».

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 3)
Ответ на: комментарий от DRVTiny

Она конечно с языковыми средствами не связана напрямую, но...

Множество твоих спорных утверждений на форуме против этого... Что же выбрать? Пожалуй, это. Лорчую, короче.

Если можно выплюнуть стектрейс - это не значит, что так надо делать всегда! И Java- и Python-программисты этим заражены.

Deleted
()
Ответ на: комментарий от DRVTiny

штатный die просто выводит сообщение об ошибке

Как и штатный exit.

PtiCa

Если можно выплюнуть стектрейс - это не значит, что так надо делать всегда!

sys.tracebacklimit

aedeph_ ★★
()
Последнее исправление: aedeph_ (всего исправлений: 1)

Быстрее, экономичнее, элегантнее будет не экономить на пробелах, а завернуть это в простую функцию. Почитай про bottom-up программирование. Вот первое что пришло в голову, возможно, есть ещё проще варианты:

use Data::Dumper;

sub make_intervals {
  my ($m, $c) = @_;
  my @result;
  my $i = 0;
  while ($m > 0) {
    push @result, [ $i, $i + $c - 1 ];
    $i += $c;
    $m -= $c;
  };
  $result[$#result][1] += $m if @result;
  \@result;
};

print Dumper make_intervals 90, 15;

Касательно предыдущей части, попробуй использовать итераторы. На CPANе мне попадались подходящие модули, но вообще их можно на коленке делать. Вот пример:

use v5.22;

my $eos = \1; # end of stream designator for igenerate

sub done { $eos };

sub igenerate(&) {
  my ($generator) = @_;
  sub {
    my ($yield) = @_;
    my $value = $generator->();
    return if $value == $eos;
    $yield->($value);
    __SUB__;
  };
};

sub imap(&@) {
  my ($function, $iterator) = @_;
  sub {
    my ($yield) = @_;
    $iterator = $iterator->(sub { $yield->($function->(@_)) });
    __SUB__ if $iterator;
  };
};

sub igather(&@) {
  my ($gatherer, $iterator) = @_;
  $iterator = $iterator->($gatherer) while $iterator;
};

sub iarray {
  my ($array) = @_;
  my $i = 0;
  igenerate { $i < @$array ? $array->[$i++] : done };
};

sub isplice {
  my ($n, $iterator) = @_;
  my @args;
  sub {
    my ($yield) = @_;
    my $i = 0;
    while ($iterator && $i < $n) {
      $iterator = $iterator->(sub { $args[$i++] = $_[0] });
    };
    $#args = $i - 1;
    $yield->(@args) if @args;
    __SUB__ if $iterator;
  };
};

igather { say "@_" } imap { map { $_ * 2 } @_ } isplice 5, iarray [ 1..20 ];
Jini ★★
()
Последнее исправление: Jini (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.