Cet article vise à vous familiariser avec «Equatable» et «Comparable» de Swift. Comme c'est un gros problème, je vais le comparer avec Java.
Swift et Java sont tous deux des nettoyages statiques puissants. «1» n'est pas «1» ». Swift a des types de référence et des types de valeur, et Java a également des types de référence et des types primitifs. Mais Swift ne dit pas "J'ai comparé String
avec==
> <". Voyons ce qui est identique et ce qui est différent.
Éléments à vérifier
est.
Swift
Utilisez l'opérateur ===
. En comparant ʻAnyObject, il peut être utilisé sans implémenter ʻEquatable
etc.
public func === (lhs: AnyObject?, rhs: AnyObject?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return Bool(Builtin.cmp_eq_RawPointer(
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(l)),
Builtin.bridgeToRawPointer(Builtin.castToUnknownObject(r))
))
case (nil, nil):
return true
default:
return false
}
}
===
est dans Equatable.swift.
Seuls les types qui implémentent ʻEquatable` peuvent effectuer une détermination d'égalité. (error: binary operator '==' cannot be applied)
public protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
Lors de l'implémentation de ʻEquatable, il doit être implémenté de manière à maintenir la substituabilité. Deux instances qui sont égales l'une à l'autre sont compatibles dans le code qui utilise cette valeur. Par conséquent,
== devrait considérer tous les aspects visibles du type. Les types ʻEquatable
ne doivent exposer aucun aspect sans valeur autre que l'identification de classe, et tout ce qui est exposé doit être documenté.
Si ʻEquatable`, les propriétés suivantes doivent être satisfaites.
b == a
si ʻa == b`)et
b == c, ʻa == c
)De plus, ʻa! = Bdoit être
! (A == b)`. Ceci est satisfait du côté de la bibliothèque standard.
extension Equatable {
@_transparent
public static func != (lhs: Self, rhs: Self) -> Bool {
return !(lhs == rhs)
}
}
ʻOptional lui-même n'est pas ʻEquatable
, mais il y en a quelques == ʻen plus de ceux de ʻEquatable
. Le paramètre ==
utilisé dépend du fait que T est ʻEquatable`.
Lors de la comparaison de nil avec ʻOptional tel que T est ʻEquatable
, ce qui suit` == ʻest utilisé.
@_inlineable
public func == <T: Equatable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l == r
case (nil, nil):
return true
default:
return false
}
}
Lors de la comparaison de nil avec ʻOptional où T n'est pas ʻEquatable
@_transparent
public func == <T>(lhs: T?, rhs: _OptionalNilComparisonType) -> Bool {
switch lhs {
case .some(_):
return false
case .none:
return true
}
}
Utiliser. _OptionalNilComparisonType
permet à T qui n'est pas ʻEquatable` d'être comparé à nil, et n'est pas utilisé dans le processus de jugement vrai / faux lui-même.
Il est possible de comparer T et nul, mais cela est traité comme un avertissement. (warning: comparing non-optional value of type to nil always returns false)
En implémentant Hashable
, cela peut être la clé de Dictionary
.
Hashable
hérite de ʻEquatable`.
public protocol Hashable : Equatable {
var hashValue: Int { get }
}
Pour deux objets a, b, si ʻa == b, alors ʻa.hashValue == b.hashValue
doit être. Il n'est pas nécessaire d'avoir ʻa == b lorsque ʻa.hashValue == b.hashValue
.
Limité aux comparaisons du même type par «Soi».
La commande aux types se fait en implémentant Comparable
. ʻArraya plusieurs méthodes de tri, mais
mutating func sort ()/
func sorted () -> [Element] ne peut être fait que lorsque ʻElement
est Comparable
.
public protocol Comparable : Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
Quatre méthodes sont définies, mais si vous implémentez ==
et <
de ʻEquatable`, les trois autres seront couvertes.
extension Comparable {
public static func > (lhs: Self, rhs: Self) -> Bool {
return rhs < lhs
}
public static func <= (lhs: Self, rhs: Self) -> Bool {
return !(rhs < lhs)
}
public static func >= (lhs: Self, rhs: Self) -> Bool {
return !(lhs < rhs)
}
}
Cela garantit qu'un seul des éléments ʻa == b / ʻa <b
/ b <a
est vrai.
De plus, l'ordre entier au sens strict est toujours valable.
! (B <a)
si ʻa <b`)et
b <c, ʻa <c
)Le type du côté implémentation peut contenir des valeurs qui sont traitées comme des exceptions et ne sont pas sujettes à comparaison, mais ces valeurs n'ont pas besoin d'être traitées dans l'ordre complet le plus étroit.
FloatingPoint.nan
renvoie false quelle que soit la manière dont il est comparé.
1.21 > Double.nan // false
1.21 < Double.nan // false
1.21 = Double.nan // false
Double.nan == Double.nan // false
Double.nan.isNaN // true
Le positionnement est la définition de la méthode de comparaison par défaut, et le tri gratuit est possible en utilisant ʻareInIncreasingOrder`, qui sera introduit plus tard.
Oui. Comparable
hérite de ʻEquatable`. Ce n'est que lorsqu'il y a un nombre égal qu'on peut parler du nombre inégal.
Il n'y a aucune comparaison avec zéro.
En passant, il est possible d'implémenter «Comparable» dans «Optionnel», mais il n'est pas possible d'implémenter un protocole pour des contraintes telles que «Optionnel où Wrapped: Equatable». (error: extension with constraints cannot have an inheritance clause)
Limité aux comparaisons du même type par «Soi».
En préparant (Element, Element) throws-> Bool
, vous pouvez trier selon la situation.
mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows
Cependant, l'ordre faible au sens étroit doit être établi.
et ʻareInIncreasingOrder (b, c)
, ʻareIncreasingOrder (a, c) `)Sans limites. Cela dépend de la mise en œuvre.
Sans limites. S'il est implémenté, ʻArray <Optional
Limité aux comparaisons du même type par (Element, Element)
.
XCTAssertEqual
requiert ʻEquatable`.
func XCTAssertEqual<T>(
_ expression1: @autoclosure () throws -> T,
_ expression2: @autoclosure () throws -> T,
_ message: @autoclosure () -> String = default,
file: StaticString = #file,
line: UInt = #line
) where T : Equatable
Il existe une version T?
Séparée pour des raisons de sécurité de type.
Java
Utilisez ==
.
Utilisez la méthode ʻequals. La méthode ʻequals
elle-même provient à l'origine de la classe ʻObject`. Cependant, la situation réelle est le même jugement.
public boolean equals(Object obj) {
return (this == obj);
}
Le côté héritage le remplace et le détermine. Un exemple de «String» est illustré ci-dessous.
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Méthode ʻEquals`
x.equals (x) == true
)y.equals (x) == true
if x.equals (y) == true
)x.equals (y) == true
et y.equals (z) == true
si x.equals (z) == true
)Il y a.
Il est censé comparer des objets non nuls. Si l'autre partie est nulle, ce sera faux.
Lors de la substitution de la méthode ʻequals, il est également nécessaire de remplacer le
public int hashCode () dérivé de ʻObject
et de s'en occuper ensemble. Ceci est dû au fait que hashCode
a les règles suivantes concernant ʻequals`.
Si deux objets sont égaux dans une méthode,
hashCode` renvoie le même entier pour chacun.Si les deux objets sont différents dans la méthode,
hashCode` doit renvoyer des entiers différents.String
remplace hashCode
comme suit:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
Puisque toutes les classes héritent de ʻObject, ʻequals
peut être utilisé.
Si Comparable <? Super T>
, comme méthode de tri pour List <T>
public static <T extends Comparable<? super T>> void sort(List<T> list)
Peut être utilisé.
public int compareTo (To)
Les éléments suivants doivent être garantis au moment de la mise en œuvre.
sgn (x.compareTo (y)) == -sgn (y.compareTo (x))
(x.compareTo (y)> 0 && y.compareTo (z)> 0)
, alors x.compareTo (z)> 0
x.compareTo (y) == 0
signifiesgn (x.compareTo (z)) == sgn (y.compareTo (z))
Lorsque les résultats de ʻe1.compareTo (e2) == 0et ʻe1.equals (e2)
correspondent à tous les e1 et e2 de la classe C, on dit qu'il est cohérent avec ʻequals. La cohérence avec ʻequals
n'est pas requise, mais il est recommandé d'être cohérent car cela ne garantit pas le comportement des ensembles de tri et des cartes de tri. Un exemple d'incohérence est «BigDecimal».
S'il est incohérent, il est recommandé de l'indiquer.
On dit qu'une exception doit être levée car null n'est une instance d'aucun objet.
«Comparable
public interface Comparable<T> {
public int compareTo(T o);
}
Comme vous pouvez le voir, il est possible d'implémenter une comparaison avec un autre type.
En préparant Comparator <T>
public static <T> void sort(List<T> list, Comparator<? super T> c)
Peut être trié par.
ʻInt compare (To1, To2) `Ce qui suit doit être garanti lors de l'implémentation.
sgn (compare (x, y)) == -sgn (compare (y, x))
((compare (x, y)> 0) && (compare (y, z)> 0))
alors compare (x, z)> 0
compare (x, y) == 0
signifiesgn (compare (x, z)) == sgn (compare (y, z))
Comparator <T>
est cohérent avec ʻequals lorsque les résultats de ʻe1.compareTo (e2) == 0
et ʻe1.equals (e2) `correspondent à tous les e1 et e2 de l'ensemble S Il y a.
Là encore, la cohérence est recommandée car le comportement des ensembles de tri et des cartes de tri n'est plus garanti. S'il est incohérent, il vaut mieux le préciser.
La comparaison avec null est autorisée selon l'option.
Pour Comparator <T>
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator)
Il existe des méthodes comme.
Il est destiné à la comparaison entre les mêmes types.
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
//Abréviation
}
Pour les types référence, il existe une méthode assert qui prend ʻObject`.
static public void assertEquals(String message, Object expected, Object actual)
Le traitement est divisé selon que l'objet attendu est nul ou non. S'il est nul, le résultat dépend du fait que réel est nul. S'il n'est pas nul, faites un jugement avec ʻattendu.equals (réel) `.
D'autre part, des méthodes sont préparées pour chaque type primitif. Par exemple, float
static public void assertEquals(String message, float expected, float actual, float delta)
Il y a un Float
à l'intérieur
public static int compare(float f1, float f2) {
if (f1 < f2)
return -1; // Neither val is NaN, thisVal is smaller
if (f1 > f2)
return 1; // Neither val is NaN, thisVal is larger
// Cannot use floatToRawIntBits because of possibility of NaNs.
int thisBits = Float.floatToIntBits(f1);
int anotherBits = Float.floatToIntBits(f2);
return (thisBits == anotherBits ? 0 : // Values are equal
(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
1)); // (0.0, -0.0) or (NaN, !NaN)
}
Appelle.
Swift | Java | |
---|---|---|
Même jugement | === |
== |
Jugement de valeur égale | == |
equals |
Propriétés imposées au jugement d'égalité | Réfléchissant/Symétrie/Transition | Réfléchissant/Symétrie/Transition/Cohérence |
Comparaison avec null dans le jugement d'égalité | Optional<T> Peut être vrai si type |
Toujours faux |
Cohérence des valeurs de hachage lorsqu'elles sont égales | Hashable Obligatoire |
Obligatoire |
Comparaison avec d'autres types dans le jugement d'égalité | Impossible | Oui |
Commande aux classes | Comparable (Tout ordre au sens strict) |
Comparable<T> (Tout ordre au sens strict) |
Cohérence de l'ordre des classes et jugement d'égalité | Toujours là | Recommandation |
Comparaison avec null dans l'ordre à la classe | Impossible | Jet d'exception recommandé |
Comparaison avec d'autres types dans l'ordre des classes | Impossible | Oui |
Commande à la collection | areInIncreasingOrder (Ordre faible au sens strict) |
Comparator<T> (Tout ordre au sens strict) |
Cohérence de l'ordre et jugement d'égalité à la collecte | Aucune mention | Recommandation |
Comparaison avec null dans l'ordre de collecte | Optional<T> Oui |
Possible selon l'option |
Comparaison avec d'autres types dans la commande à la collecte | Impossible | Impossible |
Jugement par la méthode assert | Avoir besoinEquatable |
equals utilisation |
Swift et Objective-C ont des méthodes d'assert différentes.
Quand je l'aligne à nouveau,
func XCTAssertEqual<T>(_ expression1: @autoclosure () throws -> T, _ expression2: @autoclosure () throws -> T, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line) where T : Equatable
func XCTAssertEqual<T>(_ expression1: @autoclosure () throws -> T?, _ expression2: @autoclosure () throws -> T?, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line) where T : Equatable
XCTAssertEqual(expression1, expression2, ...)
XCTAssertEqualObjects(expression1, expression2, ...)
static public void assertEquals(String message, float expected, float actual, float delta)
static public void assertEquals(String message, Object expected, Object actual)
ObjC est plus proche de Java à cet égard.
À propos de la commande totale stricte au sens étroit et de la commande stricte faible au sens étroit. C'est un terme défini quelque part.
Ce qui est strict et totalement faible est
«Comparable» de Swift est "Non réfléchissant" "Ne pas autoriser l'incomparabilité" ("FloatingPoint.nan" etc. ne sont pas applicables) Par conséquent, il s'agit d'une commande totale stricte.
d'autre part
ʻAreInIncreasingOrder` "Non réfléchissant" "Tolérer l'incomparabilité" "Impossible de faire la transition" Par conséquent, il s'agit d'une commande stricte et faible.
__peut être. __
À propos, les deux «Comparable
Pour l'incomparabilité, voir l'élément suivant.
Let's try
L'histoire est appropriée.
Takashi-kun a reçu beaucoup de balles de son frère.
struct Ball {
let diameter: Int
let color: Color
enum Color {
case blue
case red
case yellow
}
}
class Ball {
int diameter;
Color color;
public Ball(int diameter, Color color) {
this.diameter = diameter;
this.color = color;
}
enum Color {
BLUE, RED, YERROW
}
}
La taille et la couleur des boules varient. Y a-t-il la même balle? Je pense que.
extension Ball: Equatable {}
///Satisfaire la réflectivité, la symétrie et la transition
///Toutes les choses visibles ont été tissées
/// (a != b)Quand!(a == b)Correspondance des résultats
func ==(lhs: Ball, rhs: Ball) -> Bool {
return lhs.diameter == rhs.diameter && lhs.color == rhs.color
}
/**
*Satisfaire la réflectivité, la symétrie, la transition et la cohérence
*hashCode a également été remplacé
*Renvoie false si nul
*/
@Override
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof Ball) {
Ball anotherBall = (Ball)anObject;
return this.diameter == anotherBall.diameter && this.color == anotherBall.color;
}
return false;
}
/**
*Renvoie le même entier si le diamètre et la couleur n'ont pas changé
* `equals`Renvoie le même entier s'ils sont égaux
* `equals`Renvoie des entiers différents si différents
*/
@Override
public int hashCode() {
return Objects.hash(this.diameter, this.color);
}
Même ainsi, il y en a trop et je ne sais pas quel genre de balle il y a. Afin de trouver la balle à utiliser pour attraper la balle la prochaine fois, j'ai décidé de les organiser par ordre de taille.
let balls: [Ball] = [/*Beaucoup!*/]
balls.sort {
return $0.diameter < $1.diameter
}
List<Ball> list = Arrays.asList(/*Beaucoup!*/);
//Je ne vais pas autoriser null
list.sort((Ball o1, Ball o2) ->
o1.diameter - o2.diameter
);
Takashi-kun a toléré l'incomparabilité. Je n'ai pas décidé comment organiser les couleurs, donc je ne peux pas décider comment organiser des boules de la même taille.
Considérant (10, bleu) et (10, rouge), (10, bleu) <
(10, rouge) est faux et (10, bleu) >
(10, rouge) est également faux. Puisque les deux <
et >
sont faux, (10, bleu) ==
(10, rouge) est valable. En d'autres termes, les boules de même taille et de même couleur __différence __ sont __même __. C'est une histoire qui ne se produit pas si l'incomparabilité n'est pas tolérée.
L'arrangement qui se concentre uniquement sur cette taille
Est une commande stricte et faible. La requête ʻareInIncreasingOrder de Swift est un ordre strict et faible, donc ça va. Cependant, le «comparateur <T>» de Java ne convient pas car il s'appelle lui-même ordre total. Non seulement cela, mais se concentrer uniquement sur la taille signifie que les résultats de ʻe1.compareTo (e2) == 0
et ʻe1.equals (e2) `ne correspondent pas aux boules de même taille. Faire. Autrement dit, il y a une incohérence entre les méthodes «Comparator
J'étais content d'aligner les couilles, mais ma mère s'est fâchée et j'ai dû nettoyer. J'ai décidé de faire correspondre la taille et la couleur parce que c'était un gros problème.
extension Ball: Comparable {}
///Satisfait non réfléchissant, asymétrique, transitionnel
func < (lhs: Ball, rhs: Ball) -> Bool {
if lhs.diameter == rhs.diameter {
return lhs.color.hashValue < rhs.color.hashValue
} else {
return lhs.diameter < rhs.diameter
}
}
/**
*Satisfait non réfléchissant, asymétrique, transitionnel
* `equals`Cohérent avec
* `o`Jeter un gluant quand est nul
*/
@Override
public int compareTo(Ball o) {
if (this.diameter == o.diameter) {
return this.color.hashCode() - o.color.hashCode();
} else{
return this.diameter - o.diameter;
}
}
Takashi-kun est venu s'aligner en regardant les couleurs. Ceci établit la cohérence avec ʻequals. Bien sûr, il est également cohérent avec ʻEquatable
.
Suite à la décision correcte de l'ordre des couleurs, les exigences d'une commande totale stricte ont également été remplies. Cela satisfera la demande de Comparable
`` Comparable
Java jdk8/jdk8/jdk: 687fd7c7986d /src/share/classes/ java.lang (Java Platform SE 8) java.util (Java Platform SE 8) Assert (JUnit API)
Apple Swift Standard Library | Apple Developer Documentation apple/swift: The Swift Programming Language [swift-evolution] [Draft][Proposal] Formalized Ordering
commande 2004-05-29 - Cry’s Diary algorithme --cpprefjp C ++ Japanese Reference
Recommended Posts