R: расширенная отладка для линейных цепочек кода - PullRequest
0 голосов
/ 26 января 2020

Я пытаюсь выяснить, возможно ли при разумном объеме программирования создать определенную функцию отладки с помощью функций метапрограммирования R.

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

{
f1(args1)                  -> out1
f2(out1, args2)            -> out2
f3(out2, args3)            -> out3
...
fn(out<n-1>, args<n>)      -> out<n>
}

Где, например, это может быть:

f1 <- function(first_arg, second_arg, ...){my_body_code},

, и вы вызываете f1 в блоке как:

f1(second_arg = 1:5, list(a1 ="A", a2 =1), abc = letters[1:3], fav = foo_foo)

где foo_foo - это объект, определенный в вызывающей среде f1.

Я хотел бы функцию, которую я мог бы обернуть вокруг моего блока, которая для каждой строки кода создала бы запись в списке. Каждая запись будет иметь имя (line1, line2), а каждая запись строки будет иметь вложенную запись для каждого аргумента и для вывода функции. записи аргумента будут состоять, во-первых, из имени формального, которому сопоставляется фактический аргумент, во-вторых, из выражения или имени, предоставленного этому аргументу, если таковой имеется (и заполнителя, если аргумент является просто константой), и в-третьих, значение этого выражения, как если бы оно было немедленно принудительно введено в функцию. (Я бы предпочел иметь значение на момент первого выполнения обещания, но мне кажется, что это гораздо более сложная проблема, и эти два значения чаще всего будут одинаковыми).

Все аргументы, присвоенные ... (если есть), будут go в подсписке dots = list (), с именами записей, если они имеют имена и имеют соответствующую метку (..1, ..2, et c) .) если они назначены позиционно. Последним элементом каждого строкового подсписка будет имя выходного файла и его значение.

Смысл в том, чтобы создать довольно полную запись операции блока кода. Я думаю, что это аналогично разработанной версии purrr::safely, которая не ограничивается итерацией и ведет более детальную запись каждого шага, и, действительно, если функция завершается с ошибкой, вы бы хотели, чтобы сообщение об ошибке в записи списка отображалось как а также столько подходящих аргументов, сколько могло быть до появления ошибки.

Мне кажется, это было бы очень полезно при отладке линейного кода, подобного этому. Это позволяет вам делать сложные вещи, используя только отладчик RStudio. Например, он позволяет вам отслеживать код в обратном направлении. Я могу не знать, что значение в out2 неверно до тех пор, пока я не увижу более поздний вывод. Single-step не сохраняет промежуточные значения, если вы не добавите для этого кучу дополнительного кода. Кроме того, здесь хранится информация, необходимая для отслеживания совпадающих ошибок, возникающих еще до создания обещаний. К тому моменту, когда вы увидите результат, полученный в результате таких ошибок в результате пошагового перехода, информация о совпадении, вероятно, испарится.

Я действительно написал код, который принимает функцию по конвейеру и исключает каналы для ее передачи в этом формате, просто используя текстовые манипуляции. (Действительно, это была «труба Бизарро» Джона Маунта, которая заставила меня задуматься об этом). И если я, или мы, или вы, можете выяснить, как это сделать, я бы надеялся серьезно запустить второй вариант, где каждая функция вызывает следующую, предоставляя ей аргументы внутренне, а не внешне - как трассировку где вы получаете переданные значения аргумента, а также имя функции и формальные выражения. Другие языки имеют такую ​​среду отладки (например, GDB), и я хотел получить одну для R по крайней мере пять лет, может быть, 10, и это кажется шагом к этому.

1 Ответ

1 голос
/ 26 января 2020

Просто введите trace, показанный для каждой функции, которую вы хотите отследить.

f <- function(x, y) {
  z <- x + y
  z
}
trace(f, exit = quote(print(returnValue())))
f(1,2)

, указав следующее, которое показывает имя функции, вход и выход. (Последние 3 взяты из самой функции.)

Tracing f(1, 2) on exit 
[1] 3
[1] 3
...