Как упростить это выражение?

#haskell

#haskell

Вопрос:

Рассмотрим это:

 map fromEnum $ zipWith (==) "aaaa" "abaa"
-- [1,0,1,1]
  

Было бы неплохо сделать здесь только один шаг:

 zipWith (x y -> fromEnum (x == y)) "aaaa" "abaa"
  

Теперь я могу устранить y :

 zipWith (x -> fromEnum.(x ==)) "aaaa" "abaa"
  

Но я не могу устранить x . Конечно, есть способы «обмануть»…

 zipWith (curry (fromEnum . uncurry (==))) "aaaa" "abaa"
  

… но это выглядит уродливее, чем исходная лямбда.

Функция, которую я ищу, будет несколько похожа на Data.Function.on , но «наоборот». У меня такое чувство, что для этого есть смущающе простое решение. Я что-то упускаю из виду?

Комментарии:

1. Вас беспокоит эффективность или удобочитаемость?

2. Если вы спросите меня, я нахожу первый наиболее читаемым. Может быть, написано так: map fromEnum . zipWith (==) $ "aaaa" "abaa"

Ответ №1:

 zipWith (x -> fromEnum . (x ==)) "aaaa" "abaa"
  

может быть записано как

 zipWith (x -> (fromEnum .) (x ==)) "aaaa" "abaa"
  

которое можно записать как

 zipWith ((fromEnum .) . (==)) "aaaa" "abaa"
  

Если вы найдете это читаемым, я думаю, это зависит от вкуса.

РЕДАКТИРОВАТЬ: еще один хороший способ сделать это — использовать некоторые комбинаторы Мэтта Хеллиджа:

 zipWith ((==) $. id ~> id ~> fromEnum) "aaaa" "abaa"
  

Комментарии:

1. Когда я смотрю на этот пример, мне интересно, почему нет комбинатора типа (a -> a -> b) -> (b -> c) -> a -> a -> c в стиле комбинатора on :: (b -> b -> c) -> (a -> b) -> a -> a -> c from Data.Function .

2. Вы можете определить его как .: = fmap fmap fmap .

Ответ №2:

Нет предопределенных библиотечных функций, которые выполняют именно такую композицию функций, которую вы хотите здесь. Но если вы часто используете конструкции, подобные приведенной в вопросе, вы могли бы определить такую функцию:

 (.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
g .: f = x y -> g (f x y)

x = zipWith (fromEnum .: (==)) "aaaa" "abaa"
  

Комментарии:

1. Также известен как комбинатор «Скараманга» или «сиськи»: (.:) = (.) . (.)

Ответ №3:

Я определил следующие два комбинатора для приукрашивания разделов (.) :

  1. (|.>) : Вы можете прочитать следующее определение как, разрезать f справа и составить с g помощью .

     (|.>) :: (b -> c) -> (a -> a' -> b) -> (a -> a' -> c)
    f |.> g = (f .) . g
      
  2. (<.|) : Вы можете прочитать следующее определение как, срезать g слева и составить с f помощью .

     (<.|) :: (a -> b -> c) -> (a' -> b) -> (a -> a' -> c)
    f <.| g = (. g) . f
      

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

 zipWith (fromEnum |.> (==)) "aaaa" "abaa"