Используя объекты в списке как аргументы

Предположим, что у меня есть функция со следующей подписью типа:

g :: a -> a -> a -> b

У меня также есть список as — давайте назовем его xs— то, что я знаю, будет содержать по крайней мере три объекта. Я хотел бы подать заявку g к первым трем объектам xs. Я знаю, что мог определить combinator как следующее:

($$$) :: (a -> a -> a -> b) -> [a] -> b
f $$$ (x:y:z:_) = f x y z

Затем я мог просто использовать g $$$ xs. Это делает $$$ немного как uncurry, но для функции с тремя аргументами того же типа и списка вместо кортежа.

Существует ли способ сделать этот идиоматически стандарт использования combinators? Или скорее что самый идиоматический путь состоит в том, чтобы сделать это в Haskell? Я думал, пробуя pointfree на неинфиксной версии $$$ мог бы дать мне некоторое представление о том, где запустить, но вывод был отвращением с 10 flips, горстка heads и tails и aps, и 28 круглых скобок.

(NB: я знаю это, не ужасно вещь Haskelly сделать во-первых, но я столкнулся с несколькими ситуациями, где это походит на разумное решение, особенно при использовании Парсека. Я, конечно, приму, "никогда не делают этого в реальном коде", если бы это - лучший ответ, но я предпочел бы видеть, что некоторый умный прием включает ((->) r) монада или что бы то ни было.)

10
задан Travis Brown 24 May 2010 в 18:27
поделиться

2 ответа

Или, скорее, какой наиболее идиоматичный способ сделать это в Haskell?

Идиоматичный? Если вам действительно нужна функция, которая делает то, что делает ($$$), то код, который у вас есть, вероятно, настолько идиоматичен, насколько это возможно.

Я бы предпочел увидеть какой-нибудь хитрый трюк

О, ну, в том случае.

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE UndecidableInstances #-}
class ListApply f a r | f -> a r where
    ($...) :: f -> [a] -> r

instance (TypeCast b r) => ListApply b a r where
    x $... _ = typeCast x

instance (ListApply f a r) => ListApply (a -> f) a r where
    f $... (x:xs) = (f x) $... xs

Вот оно, полностью общее решение: Дана функция произвольной четности с сигнатурой типа a -> a ... -> b, примените ее к стольким элементам списка [a], сколько необходимо. Демонстрация:

ones :: [Int]
ones = repeat 1

test1 x = x
test2 x y = x + y
test3 x y z = (x + z) * (y + z)

В GHCi:

> test1 $... ones
1
> test2 $... ones
2
> test3 $... ones
4

Я, конечно, приму "никогда не делайте этого в реальном коде", если это лучший ответ

Вы, вероятно, захотите пойти с этим.


О, и немного шаблонов, необходимых для выполнения вышеприведенного кода:

class TypeCast   a b   | a -> b, b->a   where typeCast   :: a -> b
class TypeCast'  t a b | t a -> b, t b -> a where typeCast'  :: t->a->b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
instance TypeCast'  () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x  = x

Это швейцарский армейский нож метапрограммирования на уровне типов, любезно предоставленный Олегом Киселевым.

12
ответ дан 3 December 2019 в 20:03
поделиться
f $$$ (x:y:z:_) = f x y z

На мой взгляд, это наиболее идиоматичный и лаконичный способ. Если количество аргументов меняется, вы можете использовать Template Haskell или делать это итеративно - определить:

zero = const
next n f (x:xs) = n (f x) xs

, тогда ваша функция будет next (next (next zero))) , и это работает для любого вложения следующий .

Также вы можете разбить его на более примитивные комбинаторы:

firstThree (x:y:z:_) = (x,y,z)
uncurry3 f (x,y,z) = f x y z
g f = uncurry3 f . firstThree
7
ответ дан 3 December 2019 в 20:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: