Что делает хорошее имя для вспомогательной функции?
Рассмотрим следующую задачу: задан ли список длин трех кортежей (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, что бы вы назвали "салфеткой"? И что еще более важно, как вы относитесь к этой проблеме в целом?
Ответы
Ответ 1
Общие правила для именования переменных локального диапазона.
-
f
, k
, g
, h
для супер простых локальных, полу-анонимных вещей
-
go
для (хвост) рекурсивных помощников (прецедент)
-
n
, m
, i
, j
для длины и размера и других числовых значений
-
v
для результатов поиска по карте и других типов словарей
-
s
и t
для строк.
-
a:as
и x:xs
и y:ys
для списков.
-
(a,b,c,_)
для кортежей.
Они обычно применяются только для аргументов HOF. Для вашего случая я бы пошел с чем-то вроде k
или eq3
.
Используйте апострофы экономно, для производных значений.
Ответ 2
Я склонен вызывать логические функции p
для предиката. pred
, к сожалению, уже принято.
Ответ 3
В таких случаях, когда внутренняя функция в основном такая же, как внешняя функция, но с разными предварительными условиями (требующими сортировки списка), я иногда использую одно и то же имя с простым, например. 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)
Ответ 4
Моя стратегия довольно тщательно следует за предложениями:
- Если для этого есть очевидное имя, используйте это.
- Используйте
go
, если это "рабочий" или в противном случае очень похожи по отношению к исходной функции.
- Следуйте личным соглашениям на основе контекста, например.
step
и start
для аргументов сгиба.
- Если все остальное не удается, просто перейдите с общим именем, например
f
Есть два метода, которых я лично избегаю. Один использует версию апострофа исходной функции, например. hasPair'
в предложении where hasPair
. Это слишком просто, чтобы случайно написать один, когда вы имели в виду другого; Я предпочитаю использовать go
в таких случаях. Но это не огромная сделка, если функции имеют разные типы. Другой использует имена, которые могут означать что-то, но не все, что связано с тем, что на самом деле выполняет функция. napkin
попадет в эту категорию. Когда вы пересматриваете этот код, этот выбор названий, вероятно, сбивает вас с толку, так как вы забудете первоначальную причину, по которой вы назвали ее napkin
. (Потому что салфетки имеют 4 угла, потому что они легко складываются, потому что они очищают беспорядок? Они найдены в ресторанах?) Другие нарушители - это такие вещи, как bob
и myCoolFunc
.
Если вы дали функции имя более описательное, чем go
или h
, то вы должны иметь возможность посмотреть либо контекст, в котором он используется, либо тело функции, и в в обеих ситуациях довольно хорошая идея, почему это имя было выбрано. Вот где мой пункт № 3 приходит: личные соглашения. Применяется большая часть рекомендаций Дона. Если вы используете Haskell в совместной ситуации, тогда координируйте свою команду и определите определенные соглашения для обычных ситуаций.