2017-09-01 2 views
3

J'ai juste commencé à apprendre Haskell et j'ai brièvement lu certaines des règles d'indentation et il me semble que Haskell se comporte exactement comme Python quand il s'agit de l'indentation (je peux me tromper). Quoi qu'il en soit, j'ai essayé d'écrire une fonction fibonacci récursive de la queue et je continue d'obtenir une erreur d'indentation et je ne sais pas où j'ai indenté mon code erroné.L'indentation est-elle dans Haskell comme dans Python?

message d'erreur:

F1.hs:6:9: error: 
    parse error (possibly incorrect indentation or mismatched brackets) 
    | 
6 |   |n<=1 = b |  ^

code:

fib :: Integer -> Integer 
fib n = fib_help n 0 1 
    where fib_help n a b 
     |n<=1 = b 
     |otherwise fib_help (n-1) b (a+b) 

Note: Je vous écris le code dans Notepad ++ et je l'ai changé les paramètres de sorte que lorsque je TAB il a créé 4 espaces blancs au lieu d'un caractère de tabulation (comme il se doit je suppose)

Répondre

10

Non, l'indentation Haskell n'est pas comme Python. Haskell ne parle pas des niveaux d'indentation, il s'agit de faire coïncider les choses avec d'autres choses.

where fib_help n a b 

Dans cet exemple, vous avez where, et le jeton suivant n'est pas {. Cela active le mode de mise en page (c'est-à-dire l'analyse sensible des espaces). Le jeton suivant (fib_help) définit la colonne de début de la séquence suivante:

where fib_help n a b 
--  ^
--  | this is "column 0" for the current block 

La ligne suivante est la suivante:

 |n<=1 = b 

le premier jeton (|) est en retrait inférieur à « colonne 0 », qui ferme implicitement le bloc.

Votre code est analysé comme si vous aviez écrit

fib n = fib_help n 0 1 
    where { fib_help n a b } 

     |n<=1 = b 
     |otherwise fib_help (n-1) b (a+b) 

Voici plusieurs erreurs de syntaxe: Le bloc where manque un =, et vous ne pouvez pas commencer une nouvelle déclaration avec |.

Solution: Indenter tout ce qui devrait faire partie du bloc where plus que le premier jeton après where. Par exemple:

fib n = fib_help n 0 1 
    where fib_help n a b 
      |n<=1 = b 
      |otherwise = fib_help (n-1) b (a+b) 

Ou:

fib n = fib_help n 0 1 
    where 
    fib_help n a b 
     |n<=1 = b 
     |otherwise = fib_help (n-1) b (a+b) 

Ou:

fib n = fib_help n 0 1 where 
    fib_help n a b 
     |n<=1 = b 
     |otherwise = fib_help (n-1) b (a+b) 
+0

Merci! Maintenant je comprends! – Schytheron

5

Deux choses:

  1. Vous devez aligner les conduites pour qu'elles se produisent après la fonction d'assistance démarre. Here is a good explanation on why this is.
  2. otherwise a encore besoin d'un = signe après (otherwise is synonymous with True):
fib :: Integer -> Integer 
fib n = fib_help n 0 1 
    where fib_help n a b 
      |n<=1 = b 
      |otherwise = fib_help (n-1) b (a+b) 

Dire que indentation Haskell est comme Python est probablement un surgénéralisation, simplement parce que les constructions de langage sont radicalement différentes. Une déclaration plus précise serait de dire que les espaces sont significatifs dans Haskell et Python.

0

Vous avez manqué un "=" dans le cas contraire, vous pouvez voir plus d'exemples here. Le bon code:

fib :: Integer -> Integer 
fib n = fib_help n 0 1 
    where fib_help n a b 
      | n <=1 = b 
      | otherwise = fib_help (n-1) b (a+b) 
0

Vous pouvez imaginer indentation Haskell et Python de même, mais il y a quelques petites différences. Cependant, la plus grande différence est probablement que la syntaxe sensible à l'indentation de Python démarre toujours le bloc aligné sur une nouvelle ligne, alors que Haskell a des constructions syntaxiques avec des blocs alignés qui peuvent commencer partiellement à travers une ligne existante. Ce n'est pas vraiment une différence dans les règles de mise en page, mais cela affecte énormément votre façon de penser (Haskellers n'a pas tendance à simplifier autant les règles que les «niveaux de retrait» dans leur tête).

Voici un exemple de quelques-uns (horribles) syntaxe de mise en page sensible en Python:

if True: 
    x = 1 
    while (
not x 
): 
    y = 2 

Les constructions if et while sont suivies d'une série de déclarations Alignés. La première instruction non-espace de caractère de la prochaine instruction doit être indentée à une position plus éloignée que la position d'alignement du bloc externe, et définit l'alignement pour toutes les instructions suivantes dans le même bloc interne. Le premier caractère de chaque instruction doit s'aligner sur une position d'un bloc englobant (qui détermine le bloc dont il fait partie).

Si nous avons ajouté un z = 3 aligné à la position 0, il fera partie du "bloc" global. Si nous l'ajoutons à la position 4, cela fera partie du bloc if. Si nous l'ajoutons aligné à la position 5, il fera partie du bloc while. Commencer une déclaration à n'importe quelle autre position serait une erreur de syntaxe.

Notez également qu'il existe des constructions multi-lignes dont l'alignement est totalement hors de propos. Ci-dessus j'ai écrit la condition du while sur plusieurs lignes en utilisant des parenthèses, alignant même la ligne avec not x à la position 0. Peu importe que le colon introduisant le bloc indenté se trouvait sur une ligne "désalignée"; les alignements pertinents pour le bloc indenté sont la position du premier caractère non blanc de l'instruction while (position 4) et la position du premier caractère non blanc de l'instruction suivante (position 5).

Voici une mise en page sensibles (horribles) Haskell:

x = let 
    y = head . head $ do 
       _ <- [1, 2, 3] 
       pure [10] 
    z = let a = 2 
      b = 2 
    in a * b 
in y + z 

Ici, nous avons let (deux fois) et l'introduction do blocs alignés. La définition de x lui-même fait partie du « bloc » de définitions formant le module, et doit être à la position 0.

Le premier caractère non-blanc de la première définition dans un bloc let définit l'alignement de l'ensemble autres définitions dans le bloc. Dans le bloc extérieur c'est le y à la position 3. Cependant la syntaxe de let ne nécessite pas de saut de ligne avant de commencer le bloc indenté (comme les constructions indentées de Python le font en terminant l'en-tête par deux points et une nouvelle ligne).Le bloc let interne a a = 2 immédiatement après le let, mais la position de a définit toujours l'alignement requis pour les autres définitions dans le bloc (11).

Encore une fois il y a des choses que vous pouvez diviser sur plusieurs lignes où les lignes ne sont pas nécessaires pour s'aligner. Dans Haskell, vous pouvez le faire avec presque tout ce qui n'est pas une construction particulière sensible à la mise en page, alors qu'en Python vous ne pouvez le faire qu'avec des parenthèses ou en terminant une ligne avec une barre oblique inverse. Mais dans Haskell, toutes les lignes faisant partie d'une construction doivent être indentées plus loin que le bloc dont elles font partie. Par exemple, j'ai été en mesure de mettre la partie in a * b de la définition de z sur une ligne distincte. Le in fait partie de la construction syntaxique let, mais c'est et non partie du bloc aligné de définitions introduit par let, donc il n'a pas d'exigences particulières d'alignement. Cependant, toute la définition de z = ... fait partie du bloc de définitions externelet, donc je n'ai pas pu démarrer la ligne in a * b à la position 3 ou plus tôt; il s'agit d'une "ligne de continuation" de la définition z, il est donc nécessaire d'être indenté plus loin que le début de cette définition. Ceci est différent des lignes de continuation de Python, qui n'ont aucune contrainte sur leur indentation.

Le do introduit également un bloc aligné (des «instructions» plutôt que des définitions). J'aurais pu suivre la première déclaration immédiatement sur le do, mais j'ai choisi de commencer une nouvelle ligne. Le bloc ici se comporte beaucoup comme un bloc prévu de style Python; J'ai dû le démarrer à une position plus en retrait que le bloc externe (les définitions du let en position 3), et une fois que j'ai fait cela, toutes les instructions dans le bloc do doivent être alignées sur la même position (14 , ici). Puisque la ligne suivante après le pure [10] est z = ... à partir de la position 3, il termine implicitement le bloc do car il est aligné sur les définitions du bloc let, et non sur les instructions du bloc do.

Dans votre exemple:

fib :: Integer -> Integer 
fib n = fib_help n 0 1 
    where fib_help n a b 
     |n<=1 = b 
     |otherwise fib_help (n-1) b (a+b) 

La construction nécessitant un alignement est where, qui introduit un bloc de définitions comme beaucoup let. L'utilisation de blocs de style Python où vous commencez toujours une nouvelle ligne avant de commencer un nouveau bloc, votre exemple ressemblerait à ceci:

fib :: Integer -> Integer 
fib n = fib_help n 0 1 
    where 
      fib_help n a b 
     |n<=1 = b 
     |otherwise fib_help (n-1) b (a+b) 

Ce qui fait l'erreur vous submergent beaucoup plus. Vous n'avez pas démarré le bloc de définitions dans le where au prochain "niveau d'indentation" de 4 espaces, vous l'avez démarré à la position 10! Et puis est retourné à la position 8 pour le prochain "niveau de retrait". Si vous êtes plus à l'aise avec les «niveaux d'indentation» de style Python qu'avec l'alignement Haskell, formatez simplement vos blocs Haskell de la façon dont Python vous demande de formater ses blocs; après l'en-tête introduisant un bloc, terminez toujours la ligne et commencez le bloc sur la ligne suivante au prochain "tab stop".