Может ли кто-нибудь объяснить этот метод Perl?

К сожалению, я унаследовал очень большую кодовую базу Perl, и мне нужно внести некоторые изменения в одну из ее функций.

Программа читает CSV-файл и выполняет в нем некоторую очистку, чтобы создать сообщение protobuf. В части входной строки есть пара ключ-значение foo:bar.

Мне удалось отследить, где, по моему мнению, анализируются temp_params. На данный момент, пожалуйста, игнорируйте dp, давайте предположим, что dp всегда будет пустым. Ключ-значение, которое я ожидаю, является частью tp, а не dp.

sub _add_temp_and_dynamic_params {
    my ($self, $row) = @_;

    state $raw_to_full = {
        tp => 'temp_params',
        dp => 'dynamic_params',
    };

    while ( my ( $raw, $full ) = each %$raw_to_full ) {
        my @params =
            map { { key => $_->[0], value => $_->[1] } }
            grep { @$_ == 2 && $_->[0] ne '' }
            map { [ split ":" ] }
            split /,/, ( delete $row->{ $raw } or next );
        $row->{ $full }->@* = @params if @params;
    }
}

После этого я хочу вызвать temp_params{} с выходными данными этого метода, описанного выше, и цель этого метода — взять это _my_new_subroutine, чтобы temp_params{foo:bar} превратилось в отдельную независимую пару ключ-значение.

Проблема в том, что я действительно понятия не имею, что происходит в этом методе и как выглядит результат. Мне это кажется крайне загадочным. Я не знаю, как foo:bar может содержать какую-либо значимую информацию, поскольку кажется, что он просто создает хеш, в котором ключом является raw_to_full, а значением является строка tp. Я даже не уверен, будет ли мой метод работать с хешем или со строкой. Что вообще означает 'temp_params'? или ( delete $row->{ $raw } or next )? Я много раз слышал, что Perl имеет дурную славу как «язык, предназначенный только для записи», но я понятия не имел, что это будет настолько плохо.

Пока что я понимаю следующее:

Метод назначает переменную $self, поскольку методы Perl всегда передают в качестве аргумента свою собственную вызывающую программу. Затем добавляется $row. $ указывает на скаляр, и он работает со строками CSV, поэтому я предполагаю, что входные данные представляют собой строку.

Затем объявляется локальная переменная $raw_to_full, это хэш, где tp и dp — это ключи, а строки «temp_params» и «dynamic_params» — их соответствующие значения. (Это также приводит к другому вопросу: если это хэш, почему он обозначается $ вместо %?)

Затем создаются две новые локальные переменные: raw иfull, которые являются результатом «разделения» ключа и значений в каждой записи хеша raw_to_full. Для каждой итерации создается строка, в которой слово «ключ» фактически объявляется как сам ключ, а значение ключа — это «необработанное» значение, тогда как «полное» — это значение (в этом я не уверен.

Затем выполняется фильтр, включающий только строки с ключом-значением, где значение не является пустым.

Затем создается разделение в месте, где указано «:». Наконец, делается еще одно разделение, чтобы разделить, я предполагаю, разные пары kv внутри каждой строки «params». После этого я совершенно потерял понимание того, что здесь может происходить.

Да. dp на данный момент не нужен (там разные ключи, нужный мне ключ приходит в tp)

Dasph 19.08.2024 15:43

Я так и сделал, но не вижу, как существование dp меняет функциональность метода. Он по-прежнему будет выполнять один и тот же набор инструкций для обоих kv в хеше, не так ли?

Dasph 19.08.2024 15:46

Возможно также удалите комментарий «только для записи», если вы хотите, чтобы люди, знакомые с Perl, были дружелюбны. На самом деле эти строки не являются чем-то непонятным и лишь показывают, что вы не умеете их читать.

choroba 19.08.2024 15:49
delete $row->{ $raw } or next удаляет ключ из хэш-ссылки $row. Возвращается связанное значение, и если оно ложно, запускается следующая итерация охватывающего цикла. $row->{ $full }->@* = @params if @params; — это альтернативный способ записи @{ $row->{ $full } } = @params if @params, т.е. если @params есть, они присваиваются ссылке на массив, хранящейся в $row->{$full}.
choroba 19.08.2024 15:52

если это хеш, почему он обозначается $ вместо %? Прочтите о ссылках в Perl.

choroba 19.08.2024 15:54

Итак, $raw_to_full — это анонимный хеш... но если значением является буквально строка «temp_params» (или динамическая), как оно может содержать какую-либо значимую информацию?

Dasph 19.08.2024 15:58

Кажется, tp и dp переводятся в их полные эквиваленты.

choroba 19.08.2024 16:03

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

Shawn 19.08.2024 16:03

(хотя бит delete действительно выглядит так, будто автор умничает ради ума, остальное выглядит довольно просто. Помогло бы, если бы бит map/grep/map был разбит на отдельные операторы с промежуточными переменными?)

Shawn 19.08.2024 16:12

@Шон Верно... удаляю.

TLP 19.08.2024 17:16

Использование состояния кажется преждевременной оптимизацией, поскольку его цель — инициализировать переменную только один раз и сохранить это состояние во время выполнения. Поскольку переменная в этом блоке никогда не изменяется, цель состояния заключается в том, что оно инициализирует переменную только один раз. Так, возможно, попытка сэкономить несколько циклов процессора?

TLP 19.08.2024 17:20

@Dasph На будущее: не следует быть таким многословным при написании вопроса. Постарайтесь выяснить, что важно, и будьте минимальными.

TLP 19.08.2024 17:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используйте инструмент дампа, чтобы просмотреть структуру данных. Вы обнаружите, что это занимает

$row = {
   tp => "foo:bar,abc:def",
};

и меняет его на

$row = {
   temp_params => [
      { key => "foo", value => "bar" },
      { key => "abc", value => "def" },
   ],
];

Этот формат затрудняет доступ к параметрам по ключу, но позволяет использовать несколько параметров с одним и тем же ключом.

Чтобы найти значения ключа, вам нужно будет перебрать массив.

my @values_for_foo =
   map { $_->{ value } }
      grep { $_->{ key } eq "foo" }
         $row->{ temp_params }->@*;

Тем не менее, вы сохраняете только последнее значение для любого данного ключа. (Так зачем использовать эту структуру?!?) Чтобы получить единственное значение ключа (или undef, если оно не найдено),

my ( $value_for_foo ) =
   map { $_->{ value } }
      grep { $_->{ key } eq "foo" }
         $row->{ temp_params }->@*;

Задаваемые конкретные вопросы:

  • delete $row->{ $raw } — то же самое, что $row->{ $raw }, за исключением того, что при этом элемент удаляется из хэша.
  • or next переходит к следующему проходу цикла, если левый операнд имеет значение false. Предположительно, он предназначен для проверки существования $row->{ temp_params }.
  • $row->{ $full }->@* — то же самое, что и @{ $row->{ $full } }, т. е. массив, на который ссылается $row->{ $full }.
  • Я бы написал $row->{ $full }->@* = @params как $row->{ $full } = \@params.
  • Вы предполагаете, что $row — это строка, но чтобы $row->{ } было действительным, она должна быть ссылкой на хэш.
  • $raw_to_full технически содержит ссылку на хеш, а не на хеш.
  • $raw и $full действительно являются ключом и значением (например, tp и temp_params) одного из элементов.
  • Вы читаете наоборот. f( g( h() ) ) звонит h первым.
    1. split /,/, "foo:bar,abc:def" производит "foo:bar", "abc:def"
    2. Они передаются в map { [ split ":" ] }, который производит [ "foo", "bar" ], [ "abc", "def" ]
    3. Они передаются в фильтр.
    4. Они передаются в map { { key => $_->[0], value => $_->[1] } }, который производит { key => "foo", value => "bar" }, { key => "abc", value => "def" }.

Пожалуйста, ограничьте свой вопрос одной проблемой в будущем.


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

За исключением «скрытого» or next и улучшения, которое я предложил выше, на самом деле это очень читабельно. Ваша проблема в том, что вы не знаете даже основ. Незнание языка не означает нечитабельность.

Поскольку вы сравниваете с Python,

Перл:

my @params =
   map { { key => $_->[0], value => $_->[1] } }
      grep { @$_ == 2 && $_->[0] ne '' }
         map { [ split ":" ] }
            split /,/,
               $row->{ $raw };

Питон:

tmp = row[ "raw" ].split( "," )
tmp = [ x.split( ":" ) for x in tmp ]
tmp = [ x for x in tmp if len( x ) == 2 and x[ 1 ] != "" ]
params = [ { "key": x[ 0 ], "value": x[ 1 ] } for x in tmp ]

И вы называете Perl шумным?

(Я не программист на Python. Вполне возможно, что это можно было бы написать более чисто. Возможно, мне пришлось бы избегать подхода функционального программирования. Это было бы отстой.)

$row->{ $full }->@* = @params — странный способ записи $row->{ $full } = \@params. Это? Второй способ распространяет изменения в @params на $row->{$full}, первый — нет.
choroba 19.08.2024 16:05

@choroba, ты доказал мою точку зрения. Цель этого сабвуфера — создать $row->{ temp_params } из $row->{ tp }. Вы говорите, что текущий код подразумевает, что что-то уже имеет копию ссылки в $row->{ temp_params }. Но мы еще не создали эту ссылку. Так что да, $row->{ $full } = \@params определенно более понятный способ написания.

ikegami 19.08.2024 16:44

Спасибо, что помогли мне расшифровать это. Я знаю, что SO — не место для обсуждения, но если вам интересны мои мысли о Perl, я думаю, что этот пост от «dusktreader» прекрасно их объясняет: reddit.com/r/Python/comments/v2dwax/… Я не впервые пробую Perl, и каждый раз я сталкиваюсь с теми же препятствиями, которые описывают большинство людей. Чрезвычайная гибкость становится для меня проблемой.

Dasph 19.08.2024 16:50

Ответ на главный вопрос: Python не так стар, как утверждается в посте. Тогда, возможно, существовал Python, но он отличался от нынешнего языка. Perl намного старше, но его возраст немного очевиден. Читабельность тут ни при чем. Это отсутствие песочницы, тот факт, что через него просачивается наследие C, отсутствие текстовых потоков по умолчанию.

ikegami 19.08.2024 17:06

Однако это гораздо более приятный язык, чем Python. Для чего-то более современного я бы использовал C# вместо Python. Верхний ответ фактически жалуется, что везде есть доллары. Это единственная часть вашего вопроса, которую вы правильно поняли!? Так действительно ли это обоснованная жалоба?

ikegami 19.08.2024 17:07

Я добавил преобразование Python в свой ответ.

ikegami 19.08.2024 17:21

Вы удалили большую часть шума из части Perl, которая исходит из всего шаблонного кода, необходимого для подготовки ввода, тогда как в Python вам просто нужно указать, что метод получает один параметр, и вызов его с большим количеством параметров приведет к очень явные ошибки. Однако лично я работаю со Scala, а не с Python. Но да, Perl НАМНОГО шумнее и его труднее читать. Дело в том, что мы могли бы показать этот код людям, которые понятия не имеют о программировании, и они смогли бы хотя бы приблизительно понять, что делает код на Python. Что касается версии Perl, они были бы совершенно невежественны.

Dasph 19.08.2024 17:25

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

Dasph 19.08.2024 17:26

Re «Вы удалили большую часть шума из части Perl». Нет, я просто добавил пробелы. Я не конвертировал весь код, чтобы сделать его кратким и потому что в остальном не было ничего интересного. { tp => "temp_params", dp => "dynamic_params" } становится { "tp": "temp_params", "dp": "dynamic_params" } и т. д.

ikegami 19.08.2024 17:26

@ikegami Вы удалили удаление и/или следующую часть, но в остальном идентично. Отступы могут творить чудеса с читабельностью. :)

TLP 19.08.2024 17:29

@TLP, я уже сказал в своем ответе, что часть or next написана плохо. Это надо было сделать отдельно. Тот факт, что часть написана плохо, не делает Perl нечитабельным. Просто этот код. Вы также можете написать нечитаемый код на Python.

ikegami 19.08.2024 17:30

Вы не просто добавили пробелы, вы исключили начальный my ($self, $row) = @_; (и будущие вызовы my и все эти объявления внутренних переменных, что, несомненно, является огромной частью шума, на который жалуются люди, которые не любят Perl)

Dasph 19.08.2024 17:34

Но разве это шум? Программист Perl анализирует это с первого взгляда. И у Perl теперь есть подписи: sub _add_temp_and_dynamic_params( $self, $row ) вместо def _add_temp_and_dynamic_params( self, row ):).

ikegami 19.08.2024 17:48

Да, это шум. В этом суть с самого начала: это вообще не интуитивно понятно. Единственный способ понять это — хорошо знать синтаксис и все неявные механизмы работы языка. Если вы дадите его тому, кто никогда не программировал, он не разберется в этом, хотя, вероятно, получит общее представление о том, что происходит в Python. В то время как другие языки просто вызывают метод установки/обновления, например foo.update(newVals), Perl в конечном итоге вызывает $row->{$full}->@*=@params, что непонятно тем, кто не знаком с синтаксисом.

Dasph 19.08.2024 18:44

Кроме того, вам не нужно объявлять self для подписи Python, метод не должен знать о вызывающем объекте, поскольку он просто вернет новый объект. Наконец: я считаю ваше преобразование Python немного неискренним, поскольку вы просто «пишете Perl на Python». Подход к проблеме в реальном стиле Python был бы тривиален, так как вам просто нужно было бы вызвать какую-нибудь программу чтения csv, которая преобразует строку tp в dict, а затем обновить нужное поле, получив значение из dict. Нет необходимости в понимании списков или вызовах функторов.

Dasph 19.08.2024 18:48

Re «В этом суть с самого начала: это вообще не интуитивно понятно». Выполнение присвоения списка — одна из самых фундаментальных операций языка. Интуиция не играет роли. Когда вы впервые увидели 4 + 3, вы понятия не имели, что он делает. Интуиция бы вам не помогла. Это не делает его нечитабельным. Опять вы путаете не зная языка и читабельности. Это разные вещи. Вы, очевидно, ожидаете, что сможете читать на языке, которого не знаете, но не стоит этого ожидать.

ikegami 19.08.2024 19:10

Re: «Я считаю ваше преобразование Python немного неискренним, поскольку вы просто «пишете Perl на Python».», Не программирование на Perl, а функциональное программирование. Вы знаете, то, что поддерживает любой другой язык, потому что это делает все очень простым, понятным и гибким. Python предоставляет все инструменты для функционального программирования, но он чертовски шумен. Вместо того, чтобы писать код как последовательность операций, Python заставляет вас использовать вложенный цикл, что приводит к гораздо большему количеству кода и более высоким умственным нагрузкам, поскольку это больше не последовательность операций, а повторяющиеся последовательности операций.

ikegami 19.08.2024 19:10

Я не собираюсь знать все тонкости, но НАДЕЮСЬ получить общую картину того, что происходит. Это не первый раз, когда я беру кодовую базу на неизвестном языке, но это было «самое сложное». Если вы не знаете Scala, и я спрошу вас, что делает val allMyNums = List(1, 2) ++ List(3, 4), вы очень легко получите ответ, даже если вы не знаете программирования il. В Perl такого не было бы, потому что синтаксис полон знаковых знаков препинания, которые для посторонних выглядят как шум. Не случайно одной из наиболее распространенных критических замечаний в адрес Perl является его неинтуитивный синтаксис.

Dasph 19.08.2024 19:10

Никаких проблем у вас не было с пунктуацией. Вы знали, что означает $. Вы знали, что @$_ == 2 сделал. Вы знаете, что означают [] и {}, потому что это то же самое, что Python. Кто на самом деле лукавит? Чего вы не поняли, так это того, что, учитывая f g h (сокращение от f( g( h() ) ), вы думали, что f вызывается первым. И вы, вероятно, тоже не искали, что делает функция карты. Ваши проблемы не имели ничего общего с читабельностью.

ikegami 19.08.2024 19:13

Суть моей проблемы заключается в том, что практически невозможно понять, что происходит в этом коде, поскольку синтаксис полон небуквенно-цифровых символов, каждый из которых имеет очень значительный эффект.

Dasph 19.08.2024 19:18

Другие вопросы по теме