Haskell: создание функции для работы со списком

Возможный дубликат:
Почему такое определение функции запрещено в haskell?

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

Например:

let f x1 x2 x3 = x1+ x2 + x3

я хочу такое поведение

(flist f) [x1,x2,x3] = x1+x2+x3

Когда список не имеет длины 3, он может вести себя как угодно. flist должен заботиться о любой функции (не только о функциях с 3 аргументами, т.е. если g x1 x2 x3 x4 = x1+x2+x3*x4, то (flist g) [x1,x2,x3,x4] = x1+x2+x3*x4 ).

Я пробовал это,

flist f [] = f
flist f (x:xs) = flist (f x) xs

Но это не работает. Как мне этого добиться? Могу ли я использовать типы данных для этого?


person user1650846    schedule 06.09.2012    source источник
comment
Вы уверены, что хотите? Функция будет работать только со значениями одного и того же типа, и вы замените чистый f x y z на сложный синтаксис f [x,y,z].   -  person AndrewC    schedule 06.09.2012
comment
да. Входные данные будут поступать в виде списков, и эти списки будут сгенерированы автоматически.   -  person user1650846    schedule 06.09.2012


Ответы (5)


С семействами шрифтов вы можете продвинуться довольно далеко, но это, конечно, не для слабонервных:

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies      #-}

class FList a where
  type Point a
  flist :: a -> [Point a] -> Point a

instance FList (a -> a) where
  type Point (a -> a) = a
  flist f [x] = f x

instance (FList (a -> b), a ~ Point (a -> b)) => FList (a -> (a -> b)) where
  type Point (a -> (a -> b)) = Point (a -> b)
  flist f (x : xs) = flist (f x) xs

Для вашего примера получаем:

> let f x y z = x + y + z
> flist f [2, 3, 5]
10
person Stefan Holdermans    schedule 06.09.2012
comment
Большое спасибо. Это работает, это то, что я хотел. С чего лучше всего начать изучение семейств типов? - person user1650846; 06.09.2012
comment
Это выглядит красиво. Я думал, что можно заставить его работать, используя семейства шрифтов. - person Satvik; 06.09.2012
comment
@user1650846 user1650846 Я могу ошибаться, извините, если это так, но если вы не понимаете, почему ваше определение flist не сработало, я думаю, важно, чтобы вы сначала начали больше узнавать об обычных типах, а затем о классах типов Прежде чем вы узнаете о семействах типов, вы должны узнать, как обойти эту проблему одним из других способов. - person AndrewC; 06.09.2012

Вы не можете создать свой flist напрямую, потому что для него нет разумного типа.

flist :: (a -> a -> a -> ..... -> a) -> [a] -> a

в зависимости от длины списка - вы знаете тип flist только тогда, когда знаете, насколько длинный список, то есть не во время компиляции, поэтому вы не можете его скомпилировать.

С помощью Template_Haskell можно написать flist "функцию", которую можно использовать как [flist| (+) [3,4] ], но Template Haskell — это очень продвинутый материал, которого, я думаю, вам следует избегать прямо сейчас, а синтаксис еще уродливее, чем тот, который вы хотели, который уже был уродливее, чем (+) 3 4.

Если вы знаете, сколько аргументов у вас будет, вы можете использовать одну из следующих функций:

flist1 f [x] = f x
flist2 f [x,y] = f x y
flist3 f [x,y,z] = f x y z
flist4 f [a,b,c,d] = f a b c d
flist5 f [a,b,c,d,e] = f a b c d e

но если вы хотите сделать с ними что-то единообразное, например сложить их, вы можете использовать предварительно написанную функцию более высокого порядка, например sum или product, для сложения или умножения, или свернуть свою собственную, используя foldl. (Например, языковое определение sumsum = foldl (+) 0.

person AndrewC    schedule 06.09.2012
comment
В основном я хотел рассчитать ожидаемое значение функций на логическом кубе. booleanCube 0 =[[]] booleanCube n = [1:x|x‹-booleanCube (n-1)]++[(-1):x|x‹-booleanCube]. Тогда ожидаемое значение: exp f n = (sum [f x| x‹-booleanCube n])/2^n . Пользователь укажет f не как функцию из списка, а обычные функции f x y z или f x y z w. - person user1650846; 06.09.2012
comment
Почему бы не заставить их указать f для работы с двумя аргументами, а затем использовать foldl1 для работы со списком. Функции fold предназначены для того, чтобы брать функцию с двумя аргументами и запускать ее по всему списку. - person AndrewC; 06.09.2012

Это не слишком сложно для фиксированного количества аргументов, например.

flist3 f [a,b,c] = f a b c
flist3 _ _       = 0

(Я заметил, что вы используете функцию в числовом контексте, поэтому значение по умолчанию 0 совершенно нормально.)

В более общем контексте можно представить успех или несоответствие, возвращая значение Maybe, например.

flist3 f [a,b,c] = Just $ f a b c
flist3 _ _       = Nothing

Затем это можно использовать как:

import Data.Maybe

exp f n = (sum . mapMaybe (flist3 f) $ booleanCube n) / 2^n

(mapMaybe сопоставляет функцию a -> Maybe b со списком, но отбрасывает Nothing и собирает значения Just в список. Если отбрасывание Nothing не является желаемым поведением, вместо этого можно использовать mapM (с некоторыми корректировками функции). )

Однако, если предполагается, что exp может принимать функции типа a -> a -> a и a -> a -> a -> a и т. д., то дать exp подходящую сигнатуру типа будет сложно (вероятно, невозможно в языке с независимой типизацией, таком как Haskell), поскольку арность f не т исправлено.

(Как показывает @Mystic, в Haskell можно создавать функции с переменным числом переменных, используя классы типов, но это немного отличается от того, что вы хотели.)

person huon    schedule 06.09.2012

flist f [] = f
flist f (x:xs) = flist (f x) xs

Что-то подобное не сработает, потому что у вас не зафиксирован тип f.

f :: (? -> b) -> [a] -> b 

В чем вы вставляете? будет зависеть от количества элементов в списке.
Для определения таких функций можно использовать что-то вроде классов типов.

Один из способов — явно определить каждую функцию для всех типов функций, которые у вас есть, или вы можете использовать небольшой хакерский подход к типам.

Я написал грязный хак, просто чтобы показать, что это возможно, используя некоторые трюки с типами. Это все еще не очень гибко, так как вам нужно будет явно указать типы всех аргументов. Добавление некоторых функциональных зависимостей может решить эту проблему. Он вызывает исключение, когда количество элементов не соответствует порядку функции f.

{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

class FList a b c where 
    flist :: a -> b -> c

instance FList a [s] a where 
    flist x _ = x


instance (FList a [s] b) => FList (s -> a) [s] b where 
    flist f (x:xs) =  flist (f x) xs


f:: Int -> Int -> Int -> Int 
f a b c = a + b * c

test :: [Int]
test = [1,2,3]

foo :: Int 
foo = (flist f) test  

Что бы вы ни пытались сделать, вероятно, вам не понадобятся такие функции. Мой единственный совет - пересмотреть свой код и попытаться посмотреть, подходит ли что-то простое.

person Satvik    schedule 06.09.2012

Есть много статей по программированию на основе арности, например.

http://www.seas.upenn.edu/~ccasin/papers/aritygen.pdf

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

person nponeccop    schedule 06.09.2012