Если у меня есть список R mylist
, вы можете добавить obj
к нему элемент следующим образом:
mylist[[length(mylist)+1]] <- obj
Но наверняка есть и более компактный способ. Когда я был новичком в R, я пытался писать lappend()
так:
lappend <- function(lst, obj) {
lst[[length(lst)+1]] <- obj
return(lst)
}
но, конечно, это не работает из-за семантики вызова по имени R ( lst
эффективно копируется при вызове, поэтому изменения lst
не видны за пределами области lappend()
. Я знаю, что вы можете сделать взлом среды в функции R, чтобы выйти за пределы область действия вашей функции и видоизменение вызывающей среды, но это кажется большим молотком для написания простой функции добавления.
Кто-нибудь может предложить более красивый способ сделать это? Бонусные баллы, если он работает как для векторов, так и для списков.
Ответы:
Если это список строк, просто используйте
c()
функцию:Это работает и на векторы, так что я получу бонусные баллы?
Изменить (2015-февраль-01): эта публикация выходит в свой пятый день рождения. Некоторые добрые читатели повторяют любые недостатки с ним, поэтому непременно посмотрите некоторые комментарии ниже. Одно предложение для
list
типов:В целом, R-типы могут затруднить использование одной и только одной идиомы для всех типов и применений.
источник
LL
все равно будет иметь два элемента после того,C(LL, c="harry")
как называется.LL <- c(LL, c="harry")
.c()
имеет 2 аргумента: список, к которому я пытаюсь добавить, а именноlist(a=3, b=c(4, 5))
, элемент, который я пытаюсь добавить, а именноc=c(6, 7)
. Если вы воспользуетесь моим подходом, вы увидите, что 2 элемента списка добавляются (6
и7
с именамиc1
иc2
) вместо одного 2-элементного вектора, названного так,c
как это явно предусмотрено!mylist <- list(mylist, list(obj))
? Если да, было бы неплохо изменить ответОП (в обновленной редакции вопроса от апреля 2012 г.) заинтересован в том, чтобы узнать, есть ли способ добавить в список за амортизированное постоянное время, как, например, можно сделать с помощью
vector<>
контейнера C ++ . Наилучший ответ (s?) Здесь пока показывает только относительное время выполнения для различных решений, заданных для задачи фиксированного размера, но не затрагивает напрямую алгоритмическую эффективность различных решений . В комментариях под многими ответами обсуждается алгоритмическая эффективность некоторых решений, но в каждом конкретном случае (по состоянию на апрель 2015 года) они приходят к неверному выводу.Алгоритмическая эффективность учитывает характеристики роста во времени (время выполнения) или в пространстве (объем потребляемой памяти) по мере увеличения размера проблемы . Выполнение теста производительности для различных решений с учетом проблемы фиксированного размера не учитывает скорость роста различных решений. OP заинтересован в том, чтобы узнать, есть ли способ добавить объекты в список R за «постоянное время амортизации». Что это значит? Чтобы объяснить, сначала позвольте мне описать «постоянное время»:
Постоянный или O (1) рост:
Если время, необходимое для выполнения данной задачи, остается таким же, как размер задачи удваивается , то мы говорим, что алгоритм имеет постоянное время рост во , или, как указано в записи «Big O», демонстрирует O (1) увеличение во времени. Когда OP говорит «амортизированное» постоянное время, он просто означает «в долгосрочной перспективе» ... то есть, если выполнение одной операции иногда занимает намного больше времени, чем обычно (например, если предварительно выделенный буфер исчерпан и иногда требует изменения размера до большего размер буфера), пока долгосрочная средняя производительность постоянна, мы все равно будем называть это O (1).
Для сравнения я также опишу «линейное время» и «квадратичное время»:
Линейный или O (n) рост:
Если время, необходимое для выполнения данной задачи, удваивается, поскольку размер задачи удваивается , то мы говорим, что алгоритм демонстрирует линейное время , или O (n) рост.
Квадратичный или O (n 2 ) рост:
Если время, требуемое для выполнения данной задачи, увеличивается на квадрат размера задачи , мы говорим, что алгоритм показывает квадратичное время или рост O (n 2 ) .
Есть много других классов эффективности алгоритмов; Я перехожу к статье Википедии для дальнейшего обсуждения.
Я благодарю @CronAcronis за его ответ, так как я новичок в R, и было приятно иметь полностью сконструированный блок кода для анализа производительности различных решений, представленных на этой странице. Я заимствую его код для моего анализа, который я дублирую (обернутый в функцию) ниже:
Результаты, опубликованные @CronAcronis, определенно свидетельствуют о том, что
a <- list(a, list(i))
метод является самым быстрым, по крайней мере, для размера задачи 10000, но результаты для одного размера проблемы не учитывают рост решения. Для этого нам нужно запустить как минимум два теста профилирования с разными размерами проблем:Прежде всего, несколько слов о значениях min / lq / mean / median / uq / max: поскольку мы выполняем одну и ту же задачу для каждого из 5 циклов, в идеальном мире можно ожидать, что для этого потребуется точно то же самое количество времени для каждого запуска. Но первый запуск, как правило, смещается в сторону более длительного времени из-за того, что код, который мы тестируем, еще не загружен в кэш процессора. После первого запуска мы ожидаем, что время будет достаточно согласованным, но иногда наш код может быть удален из кэша из-за прерываний по таймеру или других аппаратных прерываний, которые не связаны с кодом, который мы тестируем. Протестировав фрагменты кода 5 раз, мы позволяем загружать код в кэш во время первого запуска, а затем даем каждому фрагменту 4 шансы на выполнение до завершения без вмешательства внешних событий. По этой причине,
Обратите внимание, что я решил сначала запустить с размером проблемы 2000, а затем 20000, поэтому размер моей проблемы увеличился в 10 раз с первого запуска до второго.
Производительность
list
решения: O (1) (постоянное время)Давайте сначала посмотрим на рост
list
решения, так как сразу можем сказать, что это самое быстрое решение в обоих прогонах профилирования: в первом прогоне потребовалось 854 микросекунды (0,854 миллисекунды ) для выполнения 2000 задач «добавления». Во втором запуске потребовалось 8,746 миллисекунд для выполнения 20000 задач «добавления». Наивный наблюдатель сказал бы: «Ах,list
решение демонстрирует рост O (n), поскольку с ростом размера задачи в десять раз увеличивается и время, необходимое для выполнения теста». Проблема этого анализа заключается в том, что операционному агенту требуется скорость роста вставки одного объекта, которая , а не общую проблему. Зная это, ясно, чтоlist
Решение обеспечивает именно то, что хочет OP: метод добавления объектов в список за O (1).Производительность других решений
Ни одно из других решений не приближается даже к скорости
list
решения, но в любом случае полезно их изучить:Похоже, что большинство других решений имеют производительность O (n). Например,
by_index
решение, очень популярное решение, основанное на частоте, с которой я нахожу его в других сообщениях SO, заняло 11,6 миллисекунды, чтобы добавить 2000 объектов, и 953 миллисекунды, чтобы добавить в десять раз больше объектов. Общее время проблемы выросло в 100 раз, поэтому наивный наблюдатель мог бы сказать: «Ах,by_index
решение демонстрирует рост O (n 2 ), поскольку, поскольку размер проблемы увеличился в десять раз, время, необходимое для выполнения теста, увеличилось. в 100 раз. "Как и прежде, этот анализ имеет недостатки, поскольку OP заинтересован в росте вставки одного объекта. Если мы поделим общее увеличение времени на увеличение размера задачи, мы обнаружим, что увеличение времени добавления объектов увеличилось только в 10 раз, а не в 100 раз, что соответствует росту размера проблемы, поэтомуby_index
решение является O (п). Нет перечисленных решений, которые демонстрируют рост O (n 2 ) для добавления одного объекта.источник
В других ответах только
list
подход приводит к добавлению O (1), но это приводит к глубоко вложенной структуре списка, а не к простому единому списку. Я использовал приведенные ниже структуры данных, они поддерживают O (1) (амортизированные) добавления и позволяют преобразовать результат обратно в простой список.и
Используйте их следующим образом:
Эти решения могут быть расширены до полных объектов, которые самостоятельно поддерживают все связанные со списком операции, но это останется для читателя упражнением.
Другой вариант для именованного списка:
Ориентиры
Сравнение производительности с использованием кода @ phonetagger (который основан на коде @Cron Arconis). Я также добавил
better_env_as_container
и изменилenv_as_container_
немного. Оригиналenv_as_container_
был сломан и на самом деле не хранит все цифры.результат:
Я добавил
linkedList
иexpandingList
и встроенную версию обоих. ЭтоinlinedLinkedList
в основном копияlist_
, но она также преобразует вложенную структуру обратно в простой список. Кроме того, разница между встроенной и не встроенной версиями связана с накладными расходами вызовов функций.Все варианты
expandingList
иlinkedList
показывают O (1) добавляют производительность, с эталонным временем масштабирования линейно с количеством добавленных элементов.linkedList
медленнее, чемexpandingList
, и накладные расходы вызова функции также видны. Так что если вам действительно нужна вся скорость, которую вы можете получить (и хотите придерживаться кода R), используйте встроенную версиюexpandingList
.Я также взглянул на реализацию R в C, и оба подхода должны быть O (1) для любого размера до тех пор, пока у вас не закончится память.
Я также изменил
env_as_container_
, оригинальная версия будет хранить каждый элемент с индексом «i», перезаписывая ранее добавленный элемент.better_env_as_container
Я добавил очень похож ,env_as_container_
но безdeparse
вещей. Оба демонстрируют производительность O (1), но их издержки немного больше, чем у связанных / расширяющихся списков.Накладные расходы памяти
В реализации CR накладные расходы составляют 4 слова и 2 дюйма на выделенный объект.
linkedList
Подход выделяет один список длины два в Append, в общей сложности (4 * 8 + 4 + 4 + 2 * 8 =) 56 байт на приложенном пункта на 64-разрядных компьютерах ( за исключением распределения памяти накладных, так что, вероятно ближе к 64 байт).expandingList
Подход использует одно слово за приложенный пункт, плюс копию , когда удвоение длины вектора, поэтому общее использование памяти до 16 байт на элемент. Поскольку память состоит из одного или двух объектов, накладные расходы для каждого объекта незначительны. Я не изучалenv
использование памяти, но думаю, что это будет ближе кlinkedList
.источник
list_
Вариант быстрее и может быть полезно , если вам не нужно преобразовать в обычный список, то есть , если вы используете результат в виде стека.environment
что я использовал для nestoR.) Моим узким местом почти всегда является человеческое время, потраченное на кодирование и анализ данных, но я ценю критерии, которые я нашел в этом посте. Что касается нехватки памяти, я бы не возражал, если бы в моих приложениях использовалось около килобайта на узел. Я держусь за большие массивы и т. Д.В Лиспе мы сделали это так:
хотя это были «минусы», а не просто «с». Если вам нужно начать со списка empy, используйте l <- NULL.
источник
c()
Функции копирует свои аргументы в новый векторе / список и возвращает это.Вы хотите что-то подобное, может быть?
Это не очень вежливая функция (присваивать это
parent.frame()
довольно грубо), но IIUYC - это то, что вы просите.источник
Я сделал небольшое сравнение методов, упомянутых здесь.
Полученные результаты:
источник
list = list
выиграл бы не только на 1 - 2 порядка, либо на величину!Если вы передадите переменную списка в виде строки в кавычках, вы сможете получить ее из функции, например:
так:
или для дополнительного кредита:
источник
Не уверен, почему вы не думаете, что ваш первый метод не сработает. У вас есть ошибка в функции lappend: длина (список) должна быть длиной (lst). Это работает нормально и возвращает список с добавленным объектом.
источник
lappend()
что предоставил, и похоже, что оно работает так же хорошо, как c () и append (), причем все они демонстрируют поведение O (n ^ 2).попробуйте эту функцию lappend
и другие предложения с этой страницы Добавить именованный вектор в список
До свидания.
источник
Я думаю, что вы на самом деле хотите передать по ссылке (указателю) на функцию - создать новую среду (которая передается по ссылке на функции) с добавленным в нее списком:
Теперь вы только изменяете существующий список (не создавая новый)
источник
Это простой способ добавить элементы в список R:
Или программно:
источник
append()
функции R есть функция, но на самом деле это конкатенационная функция, которая работает только с векторами.append()
работает с векторами и списками, и это истинное дополнение (которое в основном совпадает с конкатенацией, поэтому я не вижу, в чем ваша проблема)на самом деле есть тонкость с
c()
функцией. Если вы делаете:вы получите, как и ожидалось:
но если вы добавите матрицу с
x <- c(x, matrix(5,2,2)
, ваш список будет иметь еще 4 элемента значения5
! Вы бы лучше сделать:Это работает для любого другого объекта, и вы получите, как и ожидалось:
Наконец, ваша функция становится:
и это работает для любого типа объекта. Вы можете быть умнее и делать:
источник
Есть также
list.append
изrlist
( ссылка на документацию )Это очень просто и эффективно.
источник
c()
илиlist
-метод. Оба намного быстрее.rlist::list.append()
, это по сути обертка вокругbase::c()
.Для проверки я запустил тестовый код, предоставленный @Cron. Есть одно существенное отличие (в дополнение к более быстрой работе на новом процессоре i7):
by_index
теперь он работает почти так же хорошо, какlist_
:Для справки приведем код теста, дословно скопированный из ответа @ Cron (на тот случай, если он позже изменит содержание):
источник
источник
Это очень интересный вопрос, и я надеюсь, что мои размышления, приведенные ниже, могут помочь найти способ его решения. Этот метод дает плоский список без индексации, но у него есть список и список, чтобы избежать структур вложенности. Я не уверен насчет скорости, так как не знаю, как это сделать.
источник
mylist<-list(1,2,3) mylist<-c(mylist,list(5))
Таким образом, мы можем легко добавить элемент / объект, используя приведенный выше код
источник