Для чего люди используют функцию идентификации?
Например, Haskell
имеет функцию id
, у Джулии есть функция identity
, и многие вопросы о SO имеют дело с функцией идентификации. (Я полагаю, что в Python вы можете сделать lambda x:x
)
Я изрыл свой мозг, пытаясь придумать пример использования этой функции; и я потерпел неудачу.
Какова цель функции идентификации и каковы ее общие случаи использования?
Ответы
Ответ 1
Помните, что функции Haskell являются значениями первого класса и могут использоваться как данные так же, как и другие значения, и передаются как аргументы другим функциям. Часто вы создаете функции, которые вы действительно хотите использовать, применяя другие функции друг к другу. Иногда вы обнаружите, что функция, которую вы хотите использовать в месте, оказывается не более сложной, чем id
.
Например, здесь есть функция, которая отменяет каждый второй элемент списка:
negateEverySecond = zipWith id (cycle [id, negate])
Ответ 2
В Julia (в стандартной библиотеке): быстрый grep показывает, что в настоящее время наиболее популярным использованием функции identity
является значение по умолчанию аргумента by
для различных связанных с сортировкой функций, таких как sort!
, sort
, issorted
и т.д.
Аргумент by
позволяет указать функцию для извлечения ключа сортировки из каждого объекта, так что два объекта a
и b
сравниваются в соответствии с by(a) < by(b)
, а не a < b
. Функция identity
является естественной по умолчанию, так как identity(a) < identity(b)
совпадает с a < b
.
Существует также внутренний код сортировки, который имеет специальную форму, если by
- identity
, что должно позволить более эффективный код для общего случая.
Ответ 3
id
может быть хорошим местом для начала при создании функций. Например,
foldl f init xs = foldr (\x r -> \acc -> r (f acc x)) id xs $ init
chi упоминает
type K a = (a -> Int) -> Int
-- factorial, CPS
factCPS :: Int -> K Int
factCPS 0 k = k 1
factCPS n k = factCPS (n-1) (k . (*n))
-- factorial, plain
fact :: Int -> Int
fact n = factCPS n id
Этот подход фактически тесно связан с приведенным выше примером; id
в определении foldl
действительно является продолжением использования.
fact q
= foldl (*) 1 [1..q]
= foldr (\x r -> \acc -> r (acc * x)) id (build
(\c n -> if q<1
then n
else let go k | k <= q = k `c` go (k+1)
| otherwise = n
in go 1)) 1
-- foldr/build
= (if q < 1
then id
else let go k | k <= q = \acc -> go (k+1) (acc * k)
| otherwise = id = \acc -> acc
in go 1) 1
= (if q < 1
then id
else let go k acc | k <= q = go (k+1) (acc * k)
| otherwise = acc
in go 1) 1
= if q < 1
then 1
else let go k acc | k <= q = go (k+1) (acc*k)
| otherwise = acc
in go 1 1
Ответ 4
В основном, вы должны использовать его, чтобы вернуть точное значение аргументов без изменений в функции.
Например, в maybe
.
Другой пример, который я вижу, находится в (id &&& id)
- т.е. дублирование элемента в кортеж.
λ> let l = [5,3,4,1]
λ> map (id &&& id) l
[(5,5),(3,3),(4,4),(1,1)]
Ответ 5
Сопоставление списка функций:
compose :: [a -> a] -> a -> a
compose = foldr (.) id
-- to be compared with
sum :: Num a => [a] -> a
sum = foldr (+) 0
Добавление сообщений условно: (не лучшее решение, все еще не так уж плохо)
-- string=""
-- if foo: string += "foo"
-- if bar: string += "bar"
-- print string
putStrLn .
(if bar then (++"bar") else id) .
(if foo then (++"foo") else id) $
""
Стиль продолжения, базовый регистр:
type K a = (a -> Int) -> Int
-- factorial, CPS
factCPS :: Int -> K Int
factCPS 0 k = k 1
factCPS n k = factCPS (n-1) (k . (*n))
-- factorial, plain
fact :: Int -> Int
fact n = factCPS n id
Ответ 6
Достаточно общая задача - получить значение из Maybe
. Мы хотели бы написать
fromMaybe :: a -> Maybe a -> a
хотя это стандартная встроенная функция. Другой стандартной встроенной функцией является Maybe
maybe :: b -> (a -> b) -> Maybe a -> b
maybe nothing just m = case m of
Nothing -> nothing
Just a -> just a
который можно рассматривать как универсальный способ разрушения Maybe
-it, просто передает одну функцию для каждой ветки соответствия шаблона.
Наконец, должно быть легко видеть, что fromMaybe
и Maybe
связаны:
fromMaybe default m = maybe default id m
или в более бесшумном стиле
fromMaybe = flip maybe id
Ответ 7
В принципе, в любое время функция высокого порядка принимает одну или несколько функций в качестве аргументов, но на самом деле вам не нужно выполнять какую-либо работу, вместо этого вы можете передать id
.
Я думаю, что кто-то уже упомянул функцию maybe
: maybe 0 id
заменит Nothing
на 0
, но если он Just
, то мы фактически не хотим изменять данные, просто вернем его. Итак, мы проходим id
.
Аналогичная функция const
: например, const 7
- это функция, которая принимает один аргумент, полностью игнорирует его и возвращает 7. Почему, черт возьми, вы когда-нибудь захотите этого? Ну, рассмотрим эту реализацию length
:
length = sum . map (const 1)
То есть, замените каждый элемент на 1, а затем суммируем их все.
Ответ 8
Я часто пытаюсь использовать идентичные функции в своем дизайне как аргумент по умолчанию для функций преобразования, чтобы максимально повысить общность и модульность.
Может случиться так, что сегодня вам нужен конвейер обработки, который работает с вашими исходными данными, поэтому вам не нужна предварительная трансформация на вашем входе (pre_mapping = lambda x : x
). В псевдо-питонном коде
def do_something(data,pre_mapping = lambda x : x)
data = pre_mapping(data)
real_foo(data)
возможно, завтра вы поймете, что лучше работать с, например, квадраты данных, и поэтому в строке вы вызываете
do_something(data
, pre_mapping = lambda x : x**2)
Это становится очень полезным, например, если вам нужно попробовать многие из этих pre_mapping
s.