47 votes

Pourquoi la multiplication ne court-elle-t-elle que d'un côté

J'ai été déconner avec fix et après de déconner avec elle, je suis tombé sur un certain comportement étrange, à savoir que l' 0 * undefined est *** Exception: Prelude.undefined et undefined * 0 est 0. Ce qui signifie également qu' fix (0 *) est *** Exception: <<loop>> et fix (* 0) est 0.

Après avoir joué avec elle, il semble que la raison en est parce qu'il est non-trivial de faire court-circuit dans les deux sens, que ce n'est pas vraiment beaucoup de sens, sans une sorte de bizarre calcul parallèle et de commencer avec le premier non-inférieure retourné.

Est ce genre de chose vu dans d'autres lieux (réflexive des fonctions qui ne sont pas réfléchis par le bas des valeurs), et c'est quelque chose que je peux compter sur? Aussi est-il un moyen pratique de faire les deux (0 *) et (* 0) évaluer à zéro quelle que soit la valeur passée dans.

41voto

dfeuer Points 1456

Votre raisonnement est correct. Il y a un unamb paquet fournissant des outils pour le type de calcul parallèle auquel vous vous référez. En effet, il offre Data.Unamb.pmult, qui tente, en parallèle, de vérifier si chaque opérande est 1 ou 0, et si donc, immédiatement, produit un résultat. Cette approche parallèle est susceptible d'être beaucoup plus lent dans la plupart des cas, pour la simple arithmétique!!!

Le court-circuitage de (*) se produit uniquement dans GHC la version 7.10. Il est le résultat de modifications apportées à la mise en œuvre de l' Integer type de GHC version. Ce supplément de la paresse était généralement considérée comme une performance bug (car il interfère avec la rigueur de l'analyse et peut même mener à l'espace des fuites dans la théorie), donc il sera supprimé dans GHC 8.0.

7voto

PyRulez Points 893

Prendre pour exemple

(if expensiveTest1 then 0 else 2) * (if expensiveTest2 then 0 else 2)

Vous avez à choisir un camp pour évaluer. Si expensiveTest2 est une boucle infinie, vous ne serez jamais en mesure de dire si le côté droit est - 0 ou pas, de sorte que vous ne pouvez pas savoir si ou de ne pas court-circuiter le côté droit, de sorte que vous n'arrivez pas à regarder du côté gauche. Vous ne pouvez pas vérifier si les deux côtés sont 0 à la fois.

Quant à savoir si vous pouvez compter sur le court-circuit à agir d'une certaine façon, il suffit de garder à l'esprit que, undefined et error agit exactement comme une boucle infinie tant que vous n'utilisez pas de IO. Par conséquent, vous pouvez tester le court-circuit et la paresse à l'aide de undefined et error. En général, court-circuitant le comportement varie d'une fonction à une fonction. (Il existe également différents niveaux de la paresse. undefined et Just undefined peuvent donner des résultats différents.)

Voir ce pour plus de détails.

6voto

Alaya Points 849

En fait, il semble qu' fix (* 0) == 0 fonctionne uniquement pour les Integer, si vous exécutez fix (* 0) :: Double ou fix (* 0) :: Int, vous obtenez toujours ***Exception <<loop>>

C'est parce que dans instance Num Integer, (*) est défini comme (*) = timesInteger

timesInteger est définie en Data.Integer

-- | Multiply two 'Integer's
timesInteger :: Integer -> Integer -> Integer
timesInteger _       (S# 0#) = S# 0#
timesInteger (S# 0#) _       = S# 0#
timesInteger x       (S# 1#) = x
timesInteger (S# 1#) y       = y
timesInteger x      (S# -1#) = negateInteger x
timesInteger (S# -1#) y      = negateInteger y
timesInteger (S# x#) (S# y#)
  = case mulIntMayOflo# x# y# of
    0# -> S# (x# *# y#)
    _  -> timesInt2Integer x# y#
timesInteger x@(S# _) y      = timesInteger y x
-- no S# as first arg from here on
timesInteger (Jp# x) (Jp# y) = Jp# (timesBigNat x y)
timesInteger (Jp# x) (Jn# y) = Jn# (timesBigNat x y)
timesInteger (Jp# x) (S# y#)
  | isTrue# (y# >=# 0#) = Jp# (timesBigNatWord x (int2Word# y#))
  | True       = Jn# (timesBigNatWord x (int2Word# (negateInt# y#)))
timesInteger (Jn# x) (Jn# y) = Jp# (timesBigNat x y)
timesInteger (Jn# x) (Jp# y) = Jn# (timesBigNat x y)
timesInteger (Jn# x) (S# y#)
  | isTrue# (y# >=# 0#) = Jn# (timesBigNatWord x (int2Word# y#))
  | True       = Jp# (timesBigNatWord x (int2Word# (negateInt# y#)))

Regardez le code ci-dessus, si vous exécutez (* 0) x, alors timesInteger _ (S# 0#) correspondent à de sorte qu' x ne seraient pas évalués, tandis que si vous exécutez (0 *) x, puis lors de la vérification si timesInteger _ (S# 0#) matchs, x serait évalué et provoquer une boucle infinie

Nous pouvons utiliser code ci-dessous pour le tester:

module Test where
import Data.Function(fix)

-- fix (0 ~*) == 0
-- fix (~* 0) == ***Exception<<loop>>
(~*) :: (Num a, Eq a) => a -> a -> a
0 ~* _ = 0
_ ~* 0 = 0
x ~* y = x ~* y

-- fix (0 *~) == ***Exception<<loop>>
-- fix (*~ 0) == 0
(*~) :: (Num a, Eq a) => a -> a -> a
_ *~ 0 = 0
0 *~ _ = 0
x *~ y = x *~ y

Il y a quelque chose d'encore plus intéressant, dans GHCI:

*Test> let x = fix (* 0) 
*Test> x 
0
*Test> x :: Double 
*** Exception: <<loop>>
*Test>  

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X