2014-06-12 4 views
2

J'ai fonctions avec deux signatures de type différents:Comment obtenir les propriétés des fonctions

f :: Int -> [a] -> [a] 
g :: (Int, Int) -> [a] -> [a] 

Le premier paramètre détermine la longueur de la liste que la fonction fonctionne sur. Par exemple, f peut fonctionner sur des listes de longueur p-1 et g fonctionne sur des listes de longueur p^(e-1) pour les premiers arguments p et (p,e) respectivement.

J'utilise f et g comme arguments à une autre fonction h :: Int -> ([a] -> [a]) -> [a] -> [a] qui a besoin de connaître cette fonction de la longueur (h « premier argument). Que faire est actuellement je me trouve:

\p -> h (p-1) (f p) 
\(p,e) -> h (p^(e-1)) (g (p,e)) 

partout où j'utiliser h en combinaison avec f et g. Cette duplication est sujette aux erreurs et désordonnée.

L'objectif est de trouver un moyen d'éviter de passer l'argument longueur à h. Au lieu de cela, h devrait être en mesure de déterminer la longueur elle-même en fonction de l'argument de la fonction.

Un non-solution serait de changer la définition de f à:

f' :: (Int, Int) -> [a] -> [a] 
f' (2,_) = previous def 
f' (p,_) = previous def 

funcToLen :: ((Int, Int) -> [a] -> [a]) -> (Int, Int) -> Int 
funcToLen f' (p,_) = p-1 
funcToLen g (p,e) = p^(e-1) 

h' :: (Int, Int) -> ((Int, Int) -> [a] -> [a]) -> [a] -> [a] 
h' (p,e) func xs = let len = funcToLen func 
         func' = func (p,e) 
        in previous def 

-- usage 
(\p -> h' (p,??) f') 
(\(p,e) -> h' (p,e) g) 

Cela présente plusieurs inconvénients:

  • Je dois changer le premier argument de f, puis ignorer la deuxième partie du tuple
  • Lorsque j'utilise réellement h' avec f', je dois créer un argument fictif pour la deuxième partie du tuple
  • Le plus important, funcToLen ne fonctionne pas parce que je ne peux pas correspondre à des noms de fonction.

Une autre solution qui fonctionne réellement est d'utiliser Either:

f' :: Int -> (Int, [a] -> [a]) 
f' 2 xs = (1, previous def) 
f' p xs = (p-1, previous def) 

g' :: (Int, Int) -> Either Int ([a] -> [a]) 
g' (p,1) xs = (1, previous def) 
g' (p,e) xs = (p^(e-1), previous def) 

h' :: (Int, ([a] -> [a])) -> ([a] -> [a]) 
h' ef = let len = fst ef 
      f = snd ef 
     in previous def 

Cela a aussi quelques inconvénients:

  • La fonction de la longueur est dupliqué pour chaque motif de f' et g'
  • Les signatures de type f', g 'et h' sont tous plus laides
  • Je ne peux pas utiliser immédiatement f' et g' par leurs propres moyens (c.-à-d. pas comme arguments à h'). Au lieu de cela, je dois décoller le tuple.

Je cherche des moyens pour nettoyer afin que je n'ai pas dupliquer la fonction de la longueur partout, mais me permet également d'utiliser les fonctions f et g' de la manière attendue. Je pense que ce problème a déjà été "résolu", mais je ne sais pas exactement ce que je devrais rechercher.

+0

Pourquoi avez-vous exactement besoin de ce passage désordonné de "longueurs à opérer" de toute façon? On dirait qu'il vaudrait beaucoup mieux diviser la liste et passer certaines parties aux fonctions qui fonctionnent sur toute la liste. – leftaroundabout

+0

J'ai de la difficulté à comprendre ce que vous demandez. Il n'est pas possible de faire correspondre les fonctions à des fonctions, et dans votre définition de 'funcToLen', le premier modèle réussira toujours, et dans le second exemple, vous utilisez' Sither' comme constructeur (vouliez-vous dire 'Left'/'Right'?) De toute façon, avec tant de variables aléatoires d'un caractère qui traînent, c'est assez difficile à suivre. J'ai du mal à comprendre pourquoi cela serait nécessaire. Quoi qu'il en soit, puisque vous avez deux types différents, et que vous avez besoin de faire correspondre les modèles, utiliser 'Sither' semble être le meilleur choix. –

+0

@leftaroundabout En un mot, maths. L'entrée de liste est un tenseur, et 'f' et' g' agissent sur une dimension de ce tenseur. La fonction 'h' 'soulève'' f' et 'g' d'une dimension vers plusieurs dimensions. Le résultat est que 'f' et' g' finissent par travailler sur quelques éléments de la liste, espacés par une foulée et avec un décalage. 'h' a besoin de connaître la dimension de' f' pour savoir combien de fois (et avec quelle foulée/décalage) exécuter 'f'. – crockeea

Répondre

1

Si vous créez une fonction qui calcule cp^e-1, découplage efficacement cette opération de f et g:

c :: (Int, Int) -> Int 
c (p, e) = p^(e - 1) 

vous pouvez fusionner dans la même fonction f et g (en éliminant simplement g). Lorsque vous devez convertir un tuple en Int, vous utilisez c.

L'implémentation de h est également triviale et ne contient pas de code dupliqué.

+0

Je ne peux pas me débarrasser de 'g', ça fait quelque chose de complètement différent de' f'. Peut-être pourriez-vous montrer plus de code? – crockeea

Questions connexes