Делать функцию с "если" без точек

У меня есть задача в Haskell (нет, это не моя домашняя работа, я учусь на экзамене).

Задача:

Write point-free function numocc, которая подсчитывает вхождения элементов в заданные списки. Например: numocc 1 [[1, 2], [2, 3, 2, 1, 1], [3]]= [1, 2, 0]

Это мой код:

addif :: Eq a => a -> Int -> a -> Int
addif x acc y = if x == y then acc+1 else acc

count :: Eq a => a -> [a] -> Int
count = flip foldl 0 . addif

numocc :: Eq a => a -> [[a]] -> [Int]
numocc = map . count

numocc и count являются "точечными", но они используют функцию addif, которая не является.

Я не знаю, как я могу сделать функцию addif без точек. Можно ли сделать оператор if без указания точки? Может быть, есть трюк, который не использует if?

Ответ 1

Я бы использовал тот факт, что вы можете легко преобразовать Bool в Int с помощью fromEnum:

addif x acc y = acc + fromEnum (x == y)

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

-- Go prefix and use $
addif x acc y = (+) acc $ fromEnum $ (==) x y
-- Swap $ for . when dropping the last argument
addif x acc = (+) acc . fromEnum . (==) x

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

В качестве альтернативы вы можете написать такую ​​функцию, как

count x = sum . map (fromEnum . (==) x)

Это почти точечно, и есть трюки, которые приближают вас, хотя они быстро становятся довольно неприятными:

count = fmap fmap fmap sum map . fmap fmap fmap fromEnum (==)

Здесь мне кажется, что лучше использовать fmap вместо (.), хотя вы можете заменить каждый fmap на (.), и это будет тот же самый код. По существу, (fmap fmap fmap) объединяет один аргумент и две функции аргументов, если вместо этого вы укажете имя .:, вы можете записать это как

count = (sum .: map) . (fromEnum .: (==))

Разбито:

> :t fmap fmap fmap sum map
Num a => (a -> b) -> [a] -> b

Таким образом, он принимает функцию от b к числовому a, списку b s и возвращает a, не так уж плохо.

> :t fmap fmap fmap fromEnum (==)
Eq a => a -> a -> Int

И этот тип можно записать как Eq a => a -> (a -> Int), что важно отметить. Это заставляет тип возвращаемой функции соответствовать входу fmap fmap fmap sum map с помощью b ~ Int, поэтому мы можем создать для них функцию типа Eq a => a -> [a] -> Int.

Ответ 2

почему не

numocc x 
  = map (length . filter (== x))
  = map ((length .) (filter (== x)) )
  = map (((length .) . filter) (== x))
  = map (((length .) . filter) ((==) x))
  = map (((length .) . filter . (==)) x)
  = (map . ((length .) . filter . (==))) x
  = (map . (length .) . filter . (==)) x

а затем тривиальное eta-сжатие.

Ответ 3

Один трюк заключается в том, чтобы импортировать одну из функций if, например. Data.Bool.bool 1 0 (также найдено в Data.Bool.Extras).

Более загадочным трюком было бы использовать Foreign.Marshal.Utils.fromBool, который делает именно то, что вам нужно здесь. Или то же самое, менее загадочное: fromEnum (спасибо @bheklilr).

Но я думаю, что самым простым трюком было бы просто не считать себя, а просто применить стандартную функцию length после filter ing для номера.

Ответ 4

Используя экземпляр Enum для Bool, можно построить замену с нулевой точкой, если это можно использовать в более общих случаях:

chk :: Bool -> (a,a) -> a
chk = ([snd,fst]!!) . fromEnum

Используя chk, мы можем определить другую версию addIf:

addIf' :: Eq a => a -> a -> Int -> Int
addIf' = curry (flip chk ((+1),id) . uncurry (==))

Теперь мы можем просто заменить chk на addIf':

addIf :: Eq a => a -> a -> Int -> Int
addIf = curry (flip (([snd,fst]!!) . fromEnum) ((+1),id) . uncurry (==))

Ответ 5

Я думаю, что вы ищете Data.Bool s bool, который существует с 4.7.0.0 (2014-04-08).

incif :: (Eq a, Enum counter) => a -> a -> counter -> counter
incif = ((bool id succ) .) . (==)

Дополнительный . позволяет == принимать два параметра перед передачей выражения в bool.

Так как порядок параметров отличается, вам нужно использовать incif следующим образом:

(flip . incif)

(Интегрирование в incif оставлено как упражнение для читателя. [Перевод: его нетривиально, и я еще не знаю как.;])