9
votes

Comment désactiver @assert dans Julia

En tant qu'ancien programmeur C, j'utilise beaucoup d'assertions dans mon code. Maintenant, je veux les désactiver globalement pour accélérer les choses. Quelle est la meilleure pratique pour y parvenir?


1 commentaires

C'est vraiment quelque chose qui devrait être ajouté, mais il n'y a pas de drapeau pour cela actuellement.


3 Réponses :


9
votes

Il n'y a pas d'option / indicateur de ligne de commande @assert pour désactiver globalement les @assert (!).

Pour l'instant, vous pouvez définir une macro @myassert qui, selon un commutateur global, est un no-op ou un @assert normal:

asserting() = false # when set to true, this will enable all `@myassert`s

macro mayassert(test)
  esc(:(if $(@__MODULE__).asserting()
    @assert($test)
   end))
end

f(x) = @mayassert x < 2 

(extrait de https://discourse.julialang.org/t/assert-alternatives/24775/14 )


0 commentaires

0
votes

Bien qu'il serait bien d'avoir cette fonctionnalité, le besoin de @assert dans votre code peut être réduit en définissant et en distribuant vos propres types. Par exemple, supposons que vous ayez une fonction foo(t::TimeType) = t , mais que vous ne souhaitiez accepter que des heures qui sont un multiple de cinq minutes. Vous pouvez créer un nouveau type avec cette exigence:

julia> t = FiveMinuteMultiple(2016, 7, 15, 4, 23)
ERROR: DomainError with 7:
the minute argument must be a multiple of 5
Stacktrace:
 [1] FiveMinuteMultiple(::Int64, ::Int64, ::Int64, ::Int64, ::Int64) at ./REPL[2]:5
 [2] top-level scope at none:0

julia> t = FiveMinuteMultiple(2016, 7, 15, 4, 25)
FiveMinuteMultiple(2016-07-15T04:25:00)

Maintenant, vous ne pouvez littéralement pas créer un FiveMinuteMultiple qui n'est pas un multiple de cinq minutes:

using Dates

struct FiveMinuteMultiple
    t::DateTime

    function FiveMinuteMultiple(y, m, d, h, mi)
        if mi%5 != 0
            throw(DomainError(m, "the minute argument must be a multiple of 5"))
        end
        new(DateTime(y, m, d, h, mi))
    end
end

Donc, si vous définissez maintenant foo(t::FiveMinuteMultiple) = t , vous n'avez plus besoin d'un @assert pour vérifier que l'argument est un temps qui est un multiple de cinq minutes. Bien sûr, vous devez toujours payer le coût de la vérification des arguments lorsque vous construisez le FiveMinuteMultiple , mais à moins qu'il ne s'agisse d'une boucle interne chaude, vous souhaiterez probablement cette validation de données supplémentaire de toute façon.

Avantages:

  • La répartition de la méthode garantit que les arguments de vos fonctions sont du type correct.
  • Vous pouvez éviter de dupliquer la même assertion sur plusieurs fonctions foo(t::FiveMinuteMultiple) , bar(t::FiveMinuteMultiple) et baz(t::FiveMinuteMultiple) .
  • L'annotation d'argument plus spécifique avertit les utilisateurs et les développeurs que la fonction attend un type de données plus spécifique.

Désavantages:

  • En fonction de votre cas d'utilisation, vous devrez peut-être transférer diverses méthodes vers le champ de données de votre structure. Par exemple, pour FiveMinuteMultiple vous devrez peut-être transférer des méthodes telles que day , hour , etc., vers le champ t de la structure.
  • L'ajout d'un nouveau concept (type) pour représenter des assertions sur vos données peut introduire une couche d'abstraction inutile.


6 commentaires

Créer des types personnalisés pour éviter les assertions me semble un peu exagéré. Je n'introduirais pas plusieurs types de wrapper Float64 pour remplacer un tas d'assertions de valeur en virgule flottante.


Bien sûr, cela dépend du contexte. Je pense que dans certaines situations, ce modèle a du sens. Un avantage est que l'annotation d'argument plus spécifique alerte l'utilisateur que la fonction attend un type de données plus spécifique.


J'utilise des assertions pour documenter les hypothèses faites dans le code. Ce sont des choses que le code suppose "ne peut pas être vraies". Ceci est différent des erreurs attendues telles que de mauvais arguments à une API. Tout le code fait des hypothèses, et elles sont parfois fausses. Il est bon de tester ces limites, mais aussi de désactiver globalement les tests lorsque vous expédiez


Je ne suis pas sûr de ce que signifie «ne pourrait pas être vrai». Si tel était le cas, vous n’auriez pas besoin d’affirmations. Peut-être que la distinction est entre les erreurs des utilisateurs et les erreurs des développeurs?


Affirme les hypothèses de document faites par le développeur lors de l'écriture du code. Voir ici pour une bonne rédaction stackoverflow.com/questions/1081409/why-should-i-use-asserts


Ah, je vois. J'avais déjà rencontré une discussion similaire, mais j'ai un peu oublié la distinction car je n'utilise pas beaucoup d'assertions. Les assertions sont un outil pour les développeurs pour aider au débogage et assurer la cohérence interne de leur logiciel. Les exceptions concernent la gestion des erreurs utilisateur ou d'exécution.



1
votes

Vous pouvez placer vos instructions @assert dans un bloc @debug . Ensuite, l'appel @assert est désactivé, sauf si vous activez le débogage soit globalement ( ENV["JULIA_DEBUG"] = "all" ) soit juste pour votre module ( ENV["JULIA_DEBUG"] = "NameOfYourModule" )

julia> @debug begin
          @assert 1==2
       end
       #or 
       @debug @assert 1==2 # assert is not called

julia> ENV["JULIA_DEBUG"] = "all" # enable debugging
"all"

julia> @debug begin
          @assert 1==2
       end
┌ Error: Exception while generating log record in module Main at REPL[4]:1
│   exception =
│    AssertionError: 1 == 2
│    Stacktrace:
│     [1] top-level scope at REPL[4]:2
│     [2] top-level scope at logging.jl:319
|     ...
â”” @ Main REPL[4]:1


1 commentaires

Malheureusement, la violation de l'assertion ne conduit plus à l'arrêt du programme, mais plutôt au message de journal Error: Exception while generating log record in module .