2009-10-17 5 views
4

Comment avez-vous plusieurs instructions dans haskell?Instructions multiples dans Haskell

Voici ce que j'essaie de faire: donné une liste telle que [a, b, c, d], retourne tous les autres éléments, donc vous obtenez [a, c]. Je peux voir la solution, et voici ce que j'ai jusqu'à présent:

fact (xs) | length(xs) `mod` 2 == 1 = head(xs) 
    | otherwise = fact(tail(xs)) 

Cela fonctionne bien la première fois, mais il se ferme. Ce que je veux être capable de dire, c'est de retourner la tête, puis d'appeler le fait (tail (xs)) Comment je fais ça?

+2

d'autres ont répondu à ce problème, donc je vais faire un point plus général. Vous pensez impérieusement : d'abord trouver la longueur de la liste puis, si c'est impair, obtenir le premier élément, puis appeler la fonction pour obtenir le reste de la liste. liste, puis placez l'élément devant. Ce que vous devez faire est de penser en termes d'expressions et d'équations.Le résultat que vous voulez est le premier élément de la liste suivi du résultat de l'application du fait à la queue de la queue. Puis traduisez cela en Haskell. –

Répondre

15

La fonction que vous avez spécifiée ne renvoie qu'un seul élément. Vous aurez besoin de changer quelque chose comme:

fact [] = [] -- can't call tail on a list of length 0! 
fact (xs) | length(xs) `mod` 2 == 1 = head(xs) : fact(tail(xs)) 
      | otherwise = fact(tail(xs)) 

Vous trouverez peut-être utile d'écrire des signatures de type pour aider à comprendre thinkos comme ceci:

fact :: [a] -> [a] -- convert a list of anything to another (shorter) list 

noter cependant que cela est très lent - O (n^2) en fait, puisqu'il prend de la longueur à chaque pas. Une solution beaucoup plus difficile utiliserait la correspondance de modèle pour traiter deux éléments à la fois:

fact :: [a] -> [a] 
-- Take the first element of each two-element pair... 
fact (x:_:xs) = x:fact xs 
-- If we have only one element left, we had an odd-length list. 
-- So grab the last element too. 
fact [x]  = [x] 
-- Return nothing if we have an empty list 
fact _  = [] 
14

Il n'y a aucune instruction dans Haskell.

Vous ne devriez pas abuser des parenthèses dans Haskell. Au contraire, vous devriez vous habituer à la langue. Donc, votre code d'origine devrait ressembler à

fact xs | length xs `mod` 2 == 1 = head xs 
     | otherwise    = fact (tail xs) 

Comme notes bdonlan, la fonction que vous cherchez est vraiment

fact []  = [] 
fact [x]  = [x] 
fact (x:_:xs) = x : fact xs 

Supposons que nous ayons la liste [a, b, c, d]. Laissez-nous appliquer la fonction et évaluer pleinement le résultat.

fact [a, b, c, d] = a : fact [c, d] 
        = a : c : fact [] 
        = a : c : [] 
        = [a, c] 

Notez que [a, b, c, d] est exactement le même que a : b : c : d : [] parce que les deux façons de listes représentant sont interprétées de façon interchangeable par le compilateur.

+1

Juste un mot sur ce trait de soulignement '_' dans l'extrait de code' fact (x: _: xs) ', qui extrait le second élément de la liste. Comme il va être expulsé de toute façon, il serait stupide d'attribuer au second élément un nom de variable inutile. Le trait de soulignement est juste une façon de faire savoir à Haskell de sélectionner le second élément, indépendamment de ce qu'il est, sans prendre la peine de lui assigner un nom de variable, rendant le code très lisible à mon avis. – Zaid

+4

Je pense qu'il y a aussi une autre raison pour laquelle nous n'utilisons pas la solution "longueur". Non seulement l'efficacité. La tâche que vous avez spécifiée est significative aussi pour les listes infinies. Travailler avec des listes infinies n'est pas seulement un sophisme, car la nature paresseuse de Haskell peut fournir des approches, où travailler avec des listes infinies peut être justifié. La solution "length" ne fonctionne que pour les listes finies, tandis que les approches "lazier" fonctionnent aussi bien pour les listes infinies que pour les listes finies. OFF: Parfois, l'évaluation paresseuse peut fournir des approches très puissantes et non intuitives («magiques»), par ex. programmation circulaire, problème de repmin. – physis

3

Permutation un sémaphores

En fait, nous pouvons le faire deux modèles possibles suivants:

  • [1,2,3,4, ..] devient [1,3,5,7 ...]
  • [1,2,3,4, ..] devient [2,4,6,8 ...]

les deux font la même chose, mais ils "commencent le comptage" le chemin inverse. Laissez-nous mettre en œuvre les deux avec la même fonction! Bien sûr, cette fonction doit être paramétrée selon le "pattern". Deux modèles possibles existent, donc, nous avons besoin d'un booléen pour le type pour la paramétrisation. Mise en œuvre: laissez-nous utiliser un paramètre booléen comme un « drapeau », « sémaphores »:

module Alternation where 

every_second :: [a] -> [a] 
every_second = every_second_at False 

every_second_at :: Bool -> [a] -> [a] 
every_second_at _ [] = [] 
every_second_at True (x : xs) = x : every_second_at False xs 
every_second_at False (x : xs) =  every_second_at True xs 

Nous avons utilisé une fonction auxiliaire, la comptabilité du « drapeau/sémaphores »: il est swapping en conséquence. En fait, cette fonction auxiliaire peut être considérée comme une généralisation de la tâche d'origine. Je pense, c'est ce qu'on appelle une fonction "worker wrapper".

Compte à rebours avec un indice

La tâche peut être généralisée encore plus loin. Pourquoi ne pas écrire une fonction beaucoup plus générale, qui peut être paramétrée par un "modulus" m, et il "récolte" tout me elems d'une liste?

  • every_mth 1 [1,2,3,4, ...] [rendements 1,2,3,4 ...]
  • every_mth 2 [1,2,3,4, .. .] [rendements 1,3,5 ...]
  • every_mth 3 [1,2,3,4, ...] [rendements 1,4,7 ...]

Nous pouvons utiliser les mêmes idées qu'auparavant, il suffit d'utiliser un "sémaphore" plus compliqué: un nombre naturel au lieu d'un booléen. Ceci est un paramètre "compte à rebours", un indice i la comptabilité quand il est à notre tour:

module Cycle where 

import Nat (Nat) 

every_mth :: Nat -> [a] -> [a] 
every_mth 0   = undefined 
every_mth m @ (i + 1) = every_mth_at m i 

Nous utilisons une fonction auxiliaire (worker wrapper), la comptabilité de l'indice du compte à rebours i:

every_mth_at :: Nat -> Nat -> [a] -> [a] 
every_mth_at _  _ []  = [] 
every_mth_at m 0  (x : xs) = x : every_mth m xs 
every_nth_at m (i + 1) (x : xs) =  every_mth_at m i xs 

Par souci de simplicité, le type de nombre naturel est « mis en œuvre » ici comme un simple alias:

module Nat (Nat) where 

type Nat = Integer 

Peut-être, dans un certain nombre de sens théorétique, il y a aussi d'autres approches plus propres, pas exactement équivalent à la tâche que vous avez spécifié, mais réglage semble être simple:

  • let every_mth 1 [0,1,2,3,4,...] rendement [0,1,2,3,4,...]
  • let every_mth 2 [0,1,2,3,4,...] rendement [0,2,4,6,8...]
  • nous every_mth 3 [0,1,2,3,4,...] donné [0,3,6,9...]

ainsi, il est précisé ici pour qu'il devrait fournir « incidemment » les lis t de multiples du paramètre, lorsqu'il est appliqué à la liste paresseuse de tous les nombres naturels.

Dans sa mise en œuvre, il vaut la peine d'utiliser des nombres comme un index «basé sur zéro». Au lieu de "tous les me", nous disons: « utiliser i comme un indice allant de 0, 1, ..., u =m -1, où u représente la limite supérieure des indices possibles. Cet indice supérieur peut être un paramètre utile dans la fonction auxiliaire, qui compte à rebours l'indice.

module Multiple where 

import Nat (Nat) 

every_mth :: Nat -> [a] -> [a] 
every_mth 0 = undefined 
every_mth (u + 1) = countdown u 

countdown :: Nat -> [a] -> [a] 
countdown = countdown_at 0 

countdown_at :: Nat -> Nat -> [a] -> [a] 
countdown_at _  _ []  = [] 
countdown_at 0  u (x : xs) = x : countdown_at u u xs 
countdown_at (i + 1) u (x : xs) =  countdown_at i u xs