Что делает хорошее имя для вспомогательной функции?

Рассмотрим следующую проблему: для заданного списка длиной три кортежа (String, Int) существует ли пара элементов, имеющих одинаковую часть «Int»? (Например, [("bob",5),("gertrude",3),("al",5)] содержит такую ​​пару, а [("bob",5),("gertrude",3),("al",1)] нет.)

Вот как бы я реализовал такую ​​функцию:

import Data.List (sortBy)
import Data.Function (on)

hasPair::[(String,Int)]->Bool
hasPair = napkin . sortBy (compare `on` snd)
  where napkin [(_, a),(_, b),(_, c)] | a == b = True
                                      | b == c = True
                                      | otherwise = False

Я использовал сопоставление с образцом для привязки имен к части кортежей «Int», но я хочу сначала выполнить сортировку (чтобы сгруппировать одинаковые элементы), поэтому я поместил функцию сопоставления с образцом в предложение where. Но это подводит меня к вопросу: какая хорошая стратегия выбора имен для функций, которые находятся внутри where предложений? Я хочу иметь возможность быстро придумывать такие имена. Для этого примера «hasPair» кажется хорошим выбором, но он уже занят! Я обнаружил, что шаблон встречается часто - естественное имя для вспомогательной функции уже используется внешней функцией, которая ее вызывает. Поэтому я время от времени называл такие вспомогательные функции такими вещами, как "op", "foo" и даже "helper" - здесь я выбрал "салфетку", чтобы подчеркнуть ее однократный характер использования и выброса.

Итак, дорогие читатели Stackoverflow, что бы вы назвали «салфеткой»? И что еще более важно, как вы вообще подходите к этому вопросу?


person gcbenison    schedule 24.05.2012    source источник
comment
go это путь :)   -  person Riccardo T.    schedule 24.05.2012
comment
Общая метрика: используйте короткие описательные имена, если вам трудно сделать их достаточно описательными, просто сделайте их короткими. Кстати, napkin в том виде, в каком он написан в настоящее время, не очень надежен (в списке должно быть ровно три члена), если вы подумаете об этом немного подробнее, возможно, он предложит лучшее имя.   -  person stephen tetley    schedule 24.05.2012
comment
@stephen Да, я понял это насчет napkin, когда писал это. Можете ли вы сказать, что это верно в целом: если вам трудно назвать функцию, ваш дизайн, вероятно, нуждается в улучшении? Если это правда, то это довольно мощная концепция...   -  person gcbenison    schedule 24.05.2012
comment
@gcbenison - для функций верхнего уровня я согласен с вашим принципом. Запутанные имена — это сигнал о том, что что-то недостаточно проработано.   -  person stephen tetley    schedule 25.05.2012
comment
Не будет ли новый hasPair, определенный в параметре where, иметь приоритет и скрыть внешний? В таком коротком фрагменте кода мало риска запутаться...   -  person Vincent Beffara    schedule 27.05.2012
comment
@vincent Да, вы правы, я не знал, что hasPair будет работать для имени вспомогательной функции. Однако многие люди обычно считают, что скрытие внешних привязок вредно.   -  person gcbenison    schedule 27.05.2012


Ответы (4)


Общие правила именования переменных локальной области видимости.

  • f , k, g, h для очень простых локальных полуанонимных вещей
  • go для (хвостовых) рекурсивных помощников (прецедент)
  • n , m, i, j для длины и размера и других числовых значений
  • v для результатов поиска карты и других типов словарей
  • s и t для строк.
  • a:as и x:xs и y:ys для списков.
  • (a,b,c,_) для полей кортежа.

Обычно они применяются только для аргументов HOF. В вашем случае я бы выбрал что-то вроде k или eq3.

Используйте апострофы экономно для производных значений.

person Don Stewart    schedule 24.05.2012
comment
Просто чтобы быть ясным для тех, кто читает это: это не правила языка Haskell, это (очень распространенные) соглашения. - person huon; 24.05.2012
comment
Разве k не является общепринятым названием продолжений? - person Heatsink; 24.05.2012
comment
Ах да, и p и q для предикатов. - person Don Stewart; 24.05.2012
comment
m для параметра, который является монадическим действием, например. f >>= m = …, подходит для функций типа when/withSomeResource. p для парсеров. - person Christopher Done; 26.05.2012

Я склонен называть логические функции p для предиката. pred, к сожалению, уже занят.

person sclv    schedule 24.05.2012

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

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

hasPair :: [(String, Int)] -> Bool
hasPair = hasDuplicate . map snd

hasDuplicate :: Ord a => [a] -> Bool
hasDuplicate = not . isStrictlySorted . sort

isStrictlySorted :: Ord a => [a] -> Bool
isStrictlySorted xs = and $ zipWith (<) xs (tail xs)
person hammar    schedule 25.05.2012
comment
Да, похоже, это перекликается с точкой зрения @stephen о том, что лучший дизайн естественным образом приводит к лучшим именам. - person gcbenison; 25.05.2012
comment
Будет ли такое использование hasPair' совместимо с @don's Используйте апострофы экономно для производных значений? - person gcbenison; 25.05.2012
comment
Я поддерживаю это. Не бойтесь нескольких дополнительных определений верхнего уровня, это особенно упрощает отладку, поскольку вы можете проверять поведение вспомогательных функций отдельно от поддерживаемых. Используйте явный экспорт модулей, если вы беспокоитесь о раскрытии определенного API. - person Dan Burton; 27.05.2012

Моя стратегия довольно близко следует предложениям Дона:

  1. Если для этого есть очевидное имя, используйте его.
  2. Используйте go, если это «рабочая» или иным образом очень похожая по назначению исходная функция.
  3. Следуйте личным соглашениям, основанным на контексте, например. step и start для аргументов в складку.
  4. Если ничего не помогает, просто используйте общее имя, например f.

Есть два метода, которых я лично избегаю. Один использует версию исходной функции с апострофом, например. hasPair' в предложении where hasPair. Слишком легко случайно написать одно, имея в виду другое; Я предпочитаю использовать go в таких случаях. Но это не имеет большого значения, если функции имеют разные типы. Другой использует имена, которые могут что-то обозначать, но не имеют ничего общего с тем, что на самом деле делает функция. napkin попадает в эту категорию. Когда вы вернетесь к этому коду, этот выбор имени, вероятно, сбьет вас с толку, поскольку вы забудете первоначальную причину, по которой вы назвали его napkin. (Потому что у салфеток 4 угла? Потому что их легко складывать? Потому что они убирают беспорядок? Их можно найти в ресторанах?) Другими нарушителями являются такие вещи, как bob и myCoolFunc.

Если вы дали функции имя, которое является более описательным, чем go или h, то вы должны иметь возможность посмотреть либо на контекст, в котором она используется, либо на тело функции, и в обеих ситуациях получить довольно хорошее представление почему было выбрано это имя. Вот тут-то и появляется мой пункт № 3: личные условности. Большая часть советов Дона применима. Если вы используете Haskell для совместной работы, согласуйте свои действия со своей командой и примите решение об определенных соглашениях для типичных ситуаций.

person Dan Burton    schedule 26.05.2012