Как лучше всего назвать не мутирующий метод «add» в неизменяемой коллекции? - PullRequest
224 голосов
/ 06 февраля 2009

Извините за вафельный заголовок - если бы я мог придумать краткое название, мне не пришлось бы задавать вопрос.

Предположим, у меня есть тип неизменяемого списка. Он имеет операцию Foo(x), которая возвращает новый неизменный список с указанным аргументом в качестве дополнительного элемента в конце. Таким образом, чтобы создать список строк со значениями «Hello», «immutable», «world», вы можете написать:

var empty = new ImmutableList<string>();
var list1 = empty.Foo("Hello");
var list2 = list1.Foo("immutable");
var list3 = list2.Foo("word");

(Это код на C #, и меня больше всего интересует предложение на C #, если вы считаете, что язык важен. Это не вопрос языка, но и идиомы языка могут быть важны.)

Важно то, что существующие списки не изменены Foo, поэтому empty.Count все равно вернет 0.

Другой (более идиоматический) способ достижения конечного результата:

var list = new ImmutableList<string>().Foo("Hello")
                                      .Foo("immutable")
                                      .Foo("word");

Мой вопрос: Какое имя Foo лучше всего?

РЕДАКТИРОВАТЬ 3 : Как я выясню позже, имя типа может на самом деле не быть ImmutableList<T>, что делает позицию ясной. Вместо этого представьте, что он TestSuite и что он неизменен, потому что весь фреймворк, частью которого он является, неизменен ...

(конец редактирования 3)

Опции, которые я до сих пор придумал:

  • Add: распространено в .NET, но подразумевает мутацию исходного списка
  • Cons: Я считаю, что это нормальное имя в функциональных языках, но не имеет смысла для тех, кто не имеет опыта работы с такими языками
  • Plus: мой любимый до сих пор, это не подразумевает мутации для меня . Очевидно, это также используется в Haskell , но с немного иными ожиданиями (программист на Haskell может ожидать, что он добавит два списка вместе, а не добавит одно значение в другой список).
  • With: согласуется с некоторыми другими неизменными соглашениями, но не имеет такой же «добавляемости» к этому IMO.
  • And: не очень описательный.
  • Перегрузка оператора для +: мне действительно не очень нравится; Я вообще думаю, что операторы должны применяться только к типам более низкого уровня. Я готов быть убежденным, хотя!

Критерии, которые я использую для выбора:

  • Дает правильное представление о результате вызова метода (то есть, что это оригинальный список с дополнительным элементом)
  • делает как можно более ясным, что он не изменяет существующий список
  • Звучит разумно, когда объединены в цепочку, как во втором примере выше

Пожалуйста, попросите более подробную информацию, если я не проясняю себя достаточно ...

РЕДАКТИРОВАТЬ 1: Вот мои аргументы в пользу предпочтения от Plus до Add. Рассмотрим эти две строки кода:

list.Add(foo);
list.Plus(foo);

На мой взгляд (а это является личным делом) последнее явно глючит - это все равно что писать "х + 5;" как заявление само по себе. Первая строка выглядит нормально, пока вы не помните, что она неизменна. Фактически, то, что оператор плюс сам по себе не изменяет свои операнды, является еще одной причиной, по которой Plus является моим любимым. Без незначительной перегрузки операторов, он по-прежнему дает те же коннотации, которые включают (для меня) не мутирование операндов (или цель метода в этом случае).

РЕДАКТИРОВАТЬ 2: Причины неприязни Add.

Различные ответы эффективны: «Идите с Add. Вот что делает DateTime, и у String есть Replace методы и т. Д., Которые не делают очевидной неизменность». Я согласен - здесь есть приоритет. Тем не менее, я видел много людей, которые звонят DateTime.Add или String.Replace и ожидают мутации . Есть множество вопросов группы новостей (и, вероятно, SO, если я копаюсь), на которые отвечают: «Вы игнорируете возвращаемое значение String.Replace; строки неизменны, возвращается новая строка.»

Теперь я должен раскрыть тонкость вопроса - тип может не на самом деле быть неизменным списком, но другого неизменного типа. В частности, я работаю над платформой для сравнительного анализа, где вы добавляете тесты в комплект, и это создает новый комплект. Может быть очевидно, что:

var list = new ImmutableList<string>();
list.Add("foo");

ничего не добьется, но становится лот мрачнее, когда вы меняете его на:

var suite = new TestSuite<string, int>();
suite.Add(x => x.Length);

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

var suite = new TestSuite<string, int>();
suite.Plus(x => x.Length);

Вот только прошу:

var suite = new TestSuite<string, int>().Plus(x => x.Length);

В идеале, я бы хотел, чтобы моим пользователям не говорили, что набор тестов является неизменным. Я хочу, чтобы они упали в пропасть успеха. Это может не возможно, но я бы хотел попробовать.

Я прошу прощения за чрезмерное упрощение исходного вопроса, говоря только о неизменяемом типе списка. Не все коллекции настолько информативны, как ImmutableList<T>:)

Ответы [ 74 ]

1 голос
/ 01 июля 2011

Это, кажется, сводится к поиску слова, которое выражает, что объект не изменен. Кроме того, он клонируется и элемент, переданный в качестве параметра, добавляется в конец клонированного списка. Стандартные имена, такие как add и clone, никогда не будут охватывать его. Возможно, clone_and_append, что также довольно неоднозначно, поскольку часть append может означать, что параметр добавляется в исходный список. Так что, вероятно, это должно быть что-то вроде clone_and_append_to_clone или, что еще лучше, append_to_clone, хотя на самом деле это не означает, что этот метод будет возвращен клоном, а скорее, что клон уже существует как часть исходного списка.

Поэтому, продолжая размышлять над поиском имени, которое не подразумевает изменения в исходном списке, а также предлагает создать новый список, рассмотрите следующее:

  1. Offshoot - var list1 = list.Offshoot("tail") - в самом строгом смысле offshoot относится здесь к новому элементу, а не ко всему новому списку. Другим очевидным недостатком является то, что это не действие, более правильное имя - list.CreateOffshoot. Тем не менее, я считаю, что это дает четкое указание на тот факт, что новый список будет содержать исходный список и что новый элемент будет находиться в конце списка.
  2. Spawn - var list1 = list.Spawn("tail") - аналогично предыдущему. Преимущество состоит в том, что это действие, но есть более слабое следствие того, что новый список будет содержать исходный список.
  3. BranchOff - var list1 = list.BranchOff("tail") - действие, которое предполагает, что исходный список будет клонирован. Одна потенциальная неоднозначность заключается в том, что не очень понятно, как будет использоваться элемент параметра.
1 голос
/ 26 августа 2011

1) Анализ вопроса
Аннотация вопроса может быть:
Из неизменного ... получить новый неизменный, но отличается от оригинального ..
В этом контексте текущего вопроса:
Из неизменяемого Списка получите новый неизменяемый список, в конце которого будет добавлена ​​разница с новым элементом.

Можете ли вы дать эффективный «глагол» («метод» в языке программирования), который выражает действие кратко, без использования простого глагола «Добавить».

2) Анализ предложения
Первая концепция : Неизменные дают аналогичные неизменные,
>> «копия» глагол выражает сохранение в «контенте», но меньше для него определенных ограничений (неизменяемых)
>> «клон» глагол выражает консервативность в «контенте», но также и для его определенных ограничений.
>> "twin" глагол похож на клон, но не встречается в программном обеспечении, но он короткий и звучит хорошо.

Вторая концепция : результат дает нечто отличное от оригинала
>> «добавить» выразить действие , чтобы сделать разницу между оригинальным и новым объектом, но по определению мы не действуем (мутируем) как неизменяемые.
>> "плюс" выражают тот факт, что результат глагола будет увеличен без нарушения исходного объекта ... подход "наречие" более "неизменен" дружественен
>> "с" , как и прежде, он выражает тот факт, что "глагол" будет делать что-то большее, но может быть неоднозначным из-за того, что он будет делать больше. Можно также выразить тот факт, что «параметр глагола» является «более» из «глагола»

3) Ответы
С глаголом "клон"
>> augmentClone
>> growClone
>> exteClone
с "с"
>> cloneWith
>> augmentCloneWith
>> growCloneWith
>> extendCloneWith

С глаголом "близнец"
>> augmentTwin
>> growTwin
>> exteTwin
с "с"
>> twinWith
>> augmentTwinWith
>> growTwinWith
>> exteTwinWith

1 голос
/ 04 февраля 2011

Я бы лично пошел с AddVoid или наоборот VoidAdd

1 голос
/ 03 августа 2011

copyWithSuffix: будет именем пользовательского метода, аналогичным способу копирования неизменяемой строки Objective-C stringByAppendingString:.

var suite = new TestSuite<string>();
suite = suite.CopyWithSuffix("Str1")
             .CopyWithSuffix("Str2")
             .CopyWithSuffix("Str3");

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

1 голос
/ 20 мая 2011

Если вы функциональный программист, у него есть несколько имен:

(++)

произносится "добавление". Или это может быть concat, в зависимости от ваших типов:

-- join two things into one thing:
append :: a -> a -> a    

-- join many things into one thing
concat :: [a] -> a

Или вы можете иметь в виду (:), АКА cons:

(:) :: a -> [a] -> [a]    

, если вы объединяете вещи в начале списка, snoc, если он идет в конце.

По крайней мере, это то, что мы называем добавлением вещей в списки на Хаскель-ленде в течение последних 20 лет.


Обратите внимание, это не арифметика (+), поскольку она моноидальна, а не кольцо.

0 голосов
/ 03 марта 2010

list.copyAndAppend (ELT)

Как это звучит?)

0 голосов
/ 07 января 2011

Заимствование из C, как насчет Concat

0 голосов
/ 23 ноября 2010

Я бы пошел с "CreateNewListWithAdditionalItems"

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

0 голосов
/ 16 апреля 2009

Как насчет метода расширения? Вы могли бы назвать это Join в этом случае. Будучи методом расширения, пользователи должны знать, что это статический метод, и поэтому могут дать им небольшую паузу и побудить их взглянуть на возвращаемое значение. В то же время вы можете использовать метод «instance».

public static ImmutableList<T> Join(this ImmutableList<T> body, T tail)
{
    // add robust error checking in case either is null...
    return new ImmutableList<T>(body, tail);
}

и потом ...

var list = new ImmutableList<string>().Join("Hello")
                                      .Join("Extensible")
                                      .Join("World");

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

0 голосов
/ 10 января 2012

Я знаю, что это бьет, казалось бы, мертвую лошадь, но я думаю, что вы можете подходить к этому странным образом (отсюда и трудности с именами). Когда у меня есть неизменяемый объект, я не часто ожидаю, что объект будет иметь функции, которые подразумевают изменчивость (например, Add или AddAll). Я знаю, что String & DateTime делают это, но даже я стал жертвой забвения использования результатов функций DateTime / String, поэтому я бы избежал этих ловушек, если это возможно.

На вашем месте я бы следовал шаблону Builder, аналогичному тому, который использует Гуава. См. неизменные коллекции в Гуаве . По сути, концепция заключается в том, что вы создаете неизменный объект со всем, что он когда-либо будет содержать. Если вы хотите сделать новый неизменный объект, вам нужно использовать новый строитель.

var suite = new TestSuite.Builder<string, int>().add(newTest).build();

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

...