Question

Pour un enrichissement culturel et intellectuel, j'ai décidé d'apprendre un peu de Haskell. J'ai lu la "Pourquoi la programmation fonctionnelle est-elle importante pour " et j'essaye de traduire son code en vrai Haskell. J'ai joint quelques-unes de mes tentatives ci-dessous (pour les parties numériques du document; l'algorithme alpha-bêta est encore plus intéressant, mais je devrais aussi écrire un évaluateur de jeu à partir de rien!).

À ce stade, c’est plus un exercice de syntaxe Haskell qu’autre chose. J'ai déjà fait des choses simples, telles que traduire repeat dans le iterate natif de Haskell, traduire quelques fonctions utilisant beaucoup de parenthèses pour la composition (les rendant ainsi plus libres de points en cours), etc.

Mais mon code fonctionne certainement, et je me demande s'il est suffisamment "Haskell-ish". Des experts peuvent-ils me donner des indices?

-- 4.1 Newton-Raphson square roots
next n x = (x + n/x)/2.0

-- -- this is "iterate::(a->a)->a->[a]"
-- repeat f a  = a : iterate f (f a)

within eps (a:b:rest) = 
  if abs(a-b) <= eps 
    then b
    else within eps (b:rest)

sqroot a0 eps n = within eps (iterate (next n) a0)

relative eps (a:b:rest) = 
  if abs(a-b) <= eps*abs(b)
    then b
    else relative eps (b:rest)

relativesqrt a0 eps n = relative eps (iterate (next n) a0)

-- 4.2 numerical differentiation

easydiff f x h = (f (x+h) - f x) / h

differentiate h0 f x = map (easydiff f x) (iterate (/2) h0)

-- diff1a h0 eps f x = within eps (differentiate h0 f x)
diff1 h0 eps f = within eps . differentiate h0 f 

elimerror n (a:b:rest) = (b*(2**n)-a)/(2**n-1) : elimerror n (b:rest)

-- need fromIntegral to make a non-integer out of the Int which comes out of round
order (a:b:c:rest) =  fromIntegral (round (logBase 2 ((a-c)/(b-c)-1)))

improve s = elimerror (order s) s 

--diff2a h0 eps f x = within eps (improve (differentiate h0 f x))
diff2 h0 eps f = within eps . improve . differentiate h0 f 

--super s = map second (iterate improve s) -- how can we make this point-free?
super :: (RealFrac t, Floating t) => [t] -> [t] 
           -- w/o this it wants to be [double]->[double]
super = map second . iterate improve

-- second (a:b:rest) = b
second = head . tail

diff3 h0 eps f = within eps . super . differentiate h0 f

-- 4.3 integration

easyintegrate f a b = (f a + f b)*(b-a)/2

-- addpair becomes (uncurry (+))

integrate f a b = integ f a b (f a) (f b) 

integ f a b fa fb = 
  (fa+fb)*(b-a)/2 : map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))
  where m = (a+b)/2 
        fm = f m 

-- test: following should be about pi
approxpi eps = within eps (improve (integrate (\x -> 4/(1+x*x)) 0 1))
superpi eps = within eps (super (integrate (\x -> 4/(1+x*x)) 0 1))

-- is there any way to keep track of the number of iterations? state monad, but seems like a lot of work...\

Pas de solution correcte

Autres conseils

4.1 Racines carrées Newton-Raphson

Ces deux lignes

sqroot a0 eps n = within eps (iterate (next n) a0)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)

sont presque identiques, vous pouvez donc résumer les choses un peu plus loin:

sqroot method a0 eps n = method eps (iterate (next n) a0)
relativesqrt = sqroot relative
withinsqrt   = sqroot within

4.2 Différenciation numérique

Je ne vois pas l'intérêt d'avoir h0 en tant qu'argument de la fonction différencier car ce n'est que le point de départ du 0 . séquence limitante. (Il n'en va pas de même pour a0 dans le cas Newton-Rhapson, où le point de départ pourrait avoir de l'importance).

Je pense qu’il est également approprié d’abstraire sur le taux auquel cette limite est proche de zéro:

differentiate rate f x = map (easydiff f x) (iterate rate 1)

Bien sûr, on peut faire les deux:

differentiate rate h0 f x = map (easydiff f x) (iterate rate h0)

En tout cas, c'est une décision assez arbitraire.

Intégration 4.2

Vous pouvez utiliser

zipWith (+) (integ f a m fa fm) (integ f m b fm fb)

au lieu de

map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))

qui, à mon avis, est plus lisible.

Pour dans et relatif , j'utiliserais la notation protégée:

within eps (a:b:rest)
  | abs(a-b)<=eps = b
  | otherwise = within eps (b:rest)

Pendant seconde , vous pouvez écrire !! 1 . Surtout ce dernier est la préférence personnelle, je suppose.

Vous devriez également donner les signatures de type.

Modifier : si vous êtes obsolète, essayez:

within :: (Ord a, Num a) => a -> [a] -> a
within eps l@(_:xs) = snd. head . filter ((<= eps) . fst) $ zip zs xs
   where
    zs = zipWith (\ a b -> abs (a-b)) l xs

(Type vérifié, non testé - et je ne sais jamais si le filtre est correct ou s'il doit être annulé;)

Roger Costello a rédigé un résumé en deux parties du document traduction du code Miranda original en Haskell. Voici la la première partie et deuxième partie de son récit.

Donc, est-ce une traduction directe (essayez de garder le code aussi similaire que possible) ou incorporée (c'est-à-dire, apportez autant de modifications idiomatiques que possible au code, sans rendre les exemples plus difficiles à comprendre, , bien sûr) un?

En supposant que ce " projet " se poursuit ......

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top