Как эффективно разбить строку на строки в J?

Я пытаюсь разобрать CSV-файл большого размера в J, и вот маршрутизация с разделением строк, которую я придумал:

splitlines =: 3 : 0
                                     NB. y is the input string
nl_positions =. (y = (10 { a.))      NB. 1 if the character in that position is a newline, 0 otherwise
nl_idx =. (# i.@#) nl_positions      NB. A list of newline indexes in the input string
prev_idx =. (# nl_idx) {. 0 , nl_idx NB. The list above, shifted one position to the right, with 0 as the first element
result =. ''
for_i. nl_idx do.                                  NB. For each newline
    to_drop =. i_index { prev_idx                  NB. The number of characters from the start of the string to skip
    to_take =. i - to_drop                         NB. The number of characters in the current line
    result =. result , < (to_take {. to_drop }. y) NB. Take the current line, box it and add to the result
end.
)

Хотя это действительно медленно. Монитор производительности показывает, что строка 8 занимает больше всего времени, вероятно, из-за выделения всей памяти при удалении и извлечении элементов и расширении списка результатов:

 Time (seconds)
┌────────┬────────┬─────┬─────────────────────────────────────────┐
│all     │here    │rep  │splitlines                               │
├────────┼────────┼─────┼─────────────────────────────────────────┤
│0.000011│0.000011│    1│monad                                    │
│0.003776│0.003776│    1│[1] nl_positions=.(y=(10{a.))            │
│0.012429│0.012429│    1│[2] nl_idx=.(#i.@#)nl_positions          │
│0.000144│0.000144│    1│[3] prev_idx =.(#nl_idx){.0,nl_idx       │
│0.000002│0.000002│    1│[4] result=.''                           │
│0.027566│0.027566│    1│[5] for_i. nl_idx do.                    │
│0.940466│0.940466│20641│[6] to_drop=.i_index{prev_idx            │
│0.011238│0.011238│20641│[7] to_take=.i-to_drop                   │
│4.310495│4.310495│20641│[8] result=.result,<(to_take{.to_drop}.y)│
│0.006926│0.006926│20641│[9] end.                                 │
│5.313052│5.313052│    1│total monad                              │
└────────┴────────┴─────┴─────────────────────────────────────────┘

Есть лучший способ сделать это? Я ищу способ:

  1. Разрезать список без выделения памяти
  2. Может быть, заменить весь цикл for одной инструкцией по массиву

j
person Mihai    schedule 04.03.2018    source источник


Ответы (1)


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

Ключевой примитив, который выполняет большую часть того, что вы хотите сделать, - это вырезать (;.). Например:

   <;._2 InputString   NB. box each segment terminated by the last character in the string
   <;._1 InputString   NB. box each segment of InputString starting with the first character in the string
   cut;._2 InputString NB. box each segment of InputString separated by 1 or more spaces

Другие связанные ресурсы, которые могут быть вам полезны: splitstring, freads, tables/dsv и Дополнения tables/csv. freads и splitstring доступны в стандартной библиотеке (сообщение J6).

   'b' freads 'myfile.txt'  NB. returns contents of myfile.txt boxed by the last character (equivalent to <;._2 freads 'myfile.txt')
   '","' splitstring InputString  NB. boxed sub-strings of input string delimited by left argument

Дополнения tables/dsv и tables/csv можно установить с помощью диспетчера пакетов. После установки их можно использовать для разделения строк и полей внутри строк следующим образом:

   require 'tables/csv'
   readcsv 'myfile.csv'
   ',' readdsv 'myfile.txt'
   TAB readdsv 'myfile.txt'
person Tikkanz    schedule 04.03.2018
comment
Спасибо, это было действительно полезно. - person Mihai; 05.03.2018
comment
Хотя библиотеки - это то, что вам нужно, поскольку вы упомянули cut (;.) и TAB, было бы неплохо также включить упоминание LF и даже CR. a. i. TAB,LF,CR показывает, что это символы ASCII 9 10 13. Это полезно, если вы не уверены, что файл начинается или заканчивается символом, на котором вы хотите вырезать: <;._1 LF , InputString - person Dane; 05.03.2018
comment
Да, это полезно. Обратите внимание, что freads решит эту проблему для большинства файлов. Он будет преобразован в разделители LF и обеспечит наличие завершающего LF. - person Tikkanz; 05.03.2018
comment
Я обрабатываю очень большой файл (~ 1 Гб, 12 Мб строк): разрезаю его на строки (~ 10 с), а затем использую регулярное выражение для извлечения числа из каждой строки и суммирую все эти числа. J абсолютно подавлен этим. В конечном итоге мне приходится принудительно завершить работу, хотя медленный язык, такой как рубин, может справиться с этим нормально. Я пробовал как цикл for_x, используя процедурный подход, так и стандартный неявный J-подход. Ни то, ни другое не работает. Какие-нибудь советы? - person Jonah; 18.06.2019
comment
Учитывая, что режущая насадка работает быстро, я бы не подумал, что это должно быть проблемой. Может быть, вам поможет новый вопрос с примерами того, что именно вы хотите сделать? - person Tikkanz; 19.06.2019