расширенный синтаксис perl — Mojo::DOM

Я пытаюсь понять некоторый расширенный (для меня) синтаксис perl для синтаксического анализа html с использованием DOM после это руководство:

say "div days:";
say $_->text for $dom->find('div.days')->each;

say "\nspan hours:";
say $_->text for $dom->find('span.hours')->each;

Что означает этот синтаксис? Что это за петля? Классика для строительства выглядит так: for(i=0;i<10;i++){ code } не: {code} for (some_condition)

Кроме того, что означает ключевое слово «каждое» в этом контексте? Есть ли в ней что-то общее с each встроенной функцией Perl или она специфична для Mojo:: ДОМ? Я думаю, что если each находится под Mojo::Dom, это должно быть упомянуто на домашней странице Mojo::DOM. Но я не нашел никакого упоминания о each в разделе методов их сайт, поэтому это должна быть встроенная функция Perl. Но эта встроенная функция each имеет совершенно другой синтаксис — как это возможно?

Другой пример со страницы учебника

say "Open Times:";
say for $dom->find('div.openTime')
            ->map(sub{$_->children->each})
            ->map(sub{$_->text})
            ->each;

Та же проблема, что и выше для map и sub.

  • Можно ли переписать эти фрагменты кода «Perlish» в более «стиле C», чтобы я мог его понять?
  • Самое главное: как перечислить все методы, их параметры и возвращаемые значения, содержащиеся в Mojo::DOM? Это должно быть как-то сделано, потому что я читал, что даже для Perl есть IDE с intellisense (автодополнением), поэтому эта IDE должна знать типы возвращаемых значений методов и т. д.

person Wakan Tanka    schedule 10.10.2012    source источник
comment
Я отвечу в полном ответе ниже, но позвольте мне подчеркнуть, что причина, по которой вы не найдете все имена методов, заключается в том, что отсутствующие на самом деле являются методами на Mojo::Collection, который является объектом-контейнером для хранения более одного объекта dom. Снова см. ниже.   -  person Joel Berger    schedule 11.10.2012
comment
Если какой-либо из приведенных ниже ответов был полезен для вас, пожалуйста, найдите время, чтобы принять его. Вы можете сделать это, нажав на галочку слева. См. часто задаваемые вопросы, если вам нужна помощь.   -  person simbabque    schedule 06.02.2013


Ответы (3)


say "Open Times:";
say for $dom->find('div.openTime')
            ->map(sub{$_->children->each})
            ->map(sub{$_->text})
            ->each;

Все эти ключевые слова (find, map, each) на самом деле не ключевые слова, а методы из Mojo::DOM. Узнать их можно по оператору ->.

В этом случае несколько методов были объединены в цепочку. Это возможно только в том случае, если каждый из них снова вернет свой объект (в данном примере это $dom). Такой вид цепочки часто используется в JavaScript, особенно в современных фреймворках, таких как jQuery. Это упрощает чтение кода и экономит операции.

По сути, вы применяете несколько транзакций в цепочке.

  1. find все элементы 'div.openTime '
  2. map ( do stuff with each of ) them with a given sub (this is an actual Perl sub):
    1. get all children of the current element as a collection
    2. и перечислите each из них (как in, вернуть массив)
  3. map them with a given sub:
    1. extract text content from the element
  4. и перечислите each из них

Все это завернуто в постфикс foreach (как сказал @Quentin). say — это функция, которую вы можете загрузить с помощью use features qw(say). Он сочетает в себе print и символ новой строки.

Может быть, теперь стало понятнее, что здесь происходит:

my $collection1 = $dom->find('div.oopenTime');

my $collection2 = $collection1->map(
  sub {
    my $collection = $_->children;
    return $collection->each;
  }
);

my collection3 = $collection2->map(
  sub {
    return $_->text;
  }
);

foreach my $text ($collection3) {
  say $text;
}

IDE, обеспечивающие автодополнение, обычно сканируют код, о котором идет речь, чтобы узнать, какие методы есть у объекта. Взгляните на Как составить список доступных методов для данного объекта или пакета в Perl? или прочитать код модуля. Еще лучше: прочитайте документацию.

person simbabque    schedule 10.10.2012

Что означает этот синтаксис, что здесь происходит?

Это постфикс для цикла.

for (@foo) {
    say $_
}

можно записать как

say $_ for @foo;

Также что означает ключевое слово «каждое» в этом контексте

Это метод объекта . Он возвращает список вещей в коллекции Mojo::Collection.

person Quentin    schedule 10.10.2012

Кажется, что другие ответы объяснили то, что я написал в своем учебном посте. Тем не менее, я хотел добавить, что я столкнулся с другим полезным методом в Mojo::DOM (фактически в классе Mojo::Collection), который называется pluck. Этот метод уменьшает визуальную сложность

->map(sub{$_->text})

to

->pluck('text')

Кроме того, я заметил, что по крайней мере несколько моих each вызовов были посторонними и что коллекция Mojo::Collection, используемая в контексте списка, будет "делать то, что я имею в виду" и each автоматически. Изменить: я проверил это и фактически при использовании в качестве строки элементы соединяются с новой строкой. Поскольку это не совсем то, что мне нужно, я ответил на свои each звонки.

Все, что здесь сказано, это то, как я мог бы написать тот же самый сценарий обучения сейчас:

#!/usr/bin/env perl

use strict;
use warnings;

use 5.10.0;
use Mojo::DOM;

my $dom = Mojo::DOM->new(<<'HTML');
<div class="box notranslate" id="venueHours">
<h5 class="translate">Hours</h5>
<div class="status closed">Currently closed</div>
<div class="hours">
  <div class="timespan">
    <div class="openTime">
      <div class="days">Mon,Tue,Wed,Thu,Sat</div>
      <span class="hours"> 10:00 AM–6:00 PM</span>
    </div>
  </div>
  <div class="timespan">
    <div class="openTime">
      <div class="days">Fri</div>
      <span class="hours"> 10:00 AM–9:00 PM</span></div>
    </div>
    <div class="timespan">
      <div class="openTime">
        <div class="days">Sun</div>
        <span class="hours"> 10:00 AM–5:00 PM</span>
      </div>
    </div>
  </div>
</div>
HTML

say "div days:";
say for $dom->find('div.days')->pluck('text')->each;

say "\nspan hours:";
say for $dom->find('span.hours')->pluck('text')->each;

say "\nOpen Times:";
say for $dom->find('div.openTime')
            ->map(sub{$_->children->each})
            ->pluck('text')
            ->each;

Обратите внимание, что я не использую ->pluck('children'), потому что метод children возвращает объект Mojo::Collection, а это означает, что возврат из pluck будет коллекцией коллекций. Чтобы сгладить структуру, мне нужно вызвать each в результате вызова children, и поэтому я не могу удалить этот конкретный вызов ->map.

Однако теперь я задаюсь вопросом, не мог ли я избежать всех этих хлопот вместе? Mojo::DOM отлично поддерживает селекторы CSS3 (ссылка на w3schools), и я мог бы попробовать не выбирать родителя (div.openTime) напрямую, а выбирать его дочерние элементы в селектор.

say "\nOpen Times:";
say for $dom->find('div.openTime > *')->pluck('text')->each;

Таким образом, здесь есть хороший урок: позволяя селектору предоставить вам как можно более ту коллекцию, которую вы хотите, избавляет вас от необходимости преобразовывать ее позже.


Чтобы ответить на ваши последние вопросы:

Чтобы перевести это

say for $dom->find('div.openTime')
            ->map(sub{$_->children->each})
            ->map(sub{$_->text})
            ->each;

к более C-esque Perl (хотя я не буду доводить это до крайности for(i=0;i<10;i++){ ... }) это может выглядеть примерно так

my @open_times = $dom->find('div.openTime')->each;

my @all_children;
foreach my $elem ( @open_times ) {
  my @children = $elem->children->each;
  push @all_children, @children;
}

my @texts;
foreach my $child ( @all_children ) {
  push @texts, $child->text;
}

foreach my $text ( @texts ) {
  print $text . "\n";
}

Я уверен, вы понимаете, почему я предпочитаю способ Mojo (цепочка объектов).

Что касается вашего второго вопроса: у Mojolicious есть отличная (хотя иногда и слишком многословная) документация. Начните здесь, чтобы узнать обо всей системе. В частности, читайте о Mojo::DOM и Mojo::Collection должно быть достаточно для анализа DOM. Я думаю, что часть вашей проблемы заключается в том, что вы не заметили взаимозависимости объектов DOM и Collection и поэтому ошибочно предположили, что все вызовы методов относятся к объектам DOM. Если вы внимательно прочитаете, то увидите, что некоторые методы DOM (возвращающие могут иметь более одного результата) возвращают объекты Collection, и find является одним из таких методов.

person Joel Berger    schedule 10.10.2012