Programing

공분산과 역 분산의 차이

crosscheck 2020. 6. 16. 08:19
반응형

공분산과 역 분산의 차이


공분산과 공분산의 차이를 이해하는 데 문제가 있습니다.


문제는 "공분산과 공분산의 차이점은 무엇입니까?"입니다.

공분산 및 반공 분산은 집합의 한 멤버를 다른 멤버와 연결하는 매핑 함수의 속성입니다 . 보다 구체적으로, 맵핑은 그 세트에 대한 관계 와 관련하여 공변량 또는 반 변형 일 수있다 .

모든 C # 유형 집합의 다음 두 가지 하위 집합을 고려하십시오. 먼저:

{ Animal, 
  Tiger, 
  Fruit, 
  Banana }.

둘째, 이것은 분명히 관련이 있습니다.

{ IEnumerable<Animal>, 
  IEnumerable<Tiger>, 
  IEnumerable<Fruit>, 
  IEnumerable<Banana> }

사상 두 번째 세트 첫 세트에서 작업을. 즉, 첫 번째 세트의 각 T에 대해 두 번째 세트의 해당 유형은 IEnumerable<T>입니다. 또는 간단히 말해서 매핑은 T → IE<T>입니다. 이것은 "가는 화살표"입니다.

지금까지 나와 함께?

이제 관계를 고려해 봅시다 . 할당 호환성 관계 첫 번째 세트의 유형 쌍 사이는. type의 값은 type Tiger의 변수에 할당 될 수 Animal있으므로 이러한 유형은 "할당 지정"이라고합니다. 더 짧은 X형식으로 Y" 유형의 값을 유형 의 변수에 할당 할 수 있습니다 "라고 작성하겠습니다 X ⇒ Y. 이것은 "뚱뚱한 화살표"입니다.

첫 번째 하위 집합에는 다음과 같은 모든 할당 호환성 관계가 있습니다.

Tiger  ⇒ Tiger
Tiger  ⇒ Animal
Animal ⇒ Animal
Banana ⇒ Banana
Banana ⇒ Fruit
Fruit  ⇒ Fruit

특정 인터페이스의 공변량 할당 호환성을 지원하는 C # 4에서는 두 번째 세트의 유형 쌍간에 할당 호환성 관계가 있습니다.

IE<Tiger>  ⇒ IE<Tiger>
IE<Tiger>  ⇒ IE<Animal>
IE<Animal> ⇒ IE<Animal>
IE<Banana> ⇒ IE<Banana>
IE<Banana> ⇒ IE<Fruit>
IE<Fruit>  ⇒ IE<Fruit>

매핑 T → IE<T> 은 할당 호환성의 존재와 방향을 유지합니다 . X ⇒ Y, 그렇다면 IE<X> ⇒ IE<Y>.

뚱뚱한 화살표의 양쪽에 두 가지가 있으면 양쪽을 해당 얇은 화살표의 오른쪽에있는 것으로 바꿀 수 있습니다.

특정 관계에 대해이 속성을 갖는 매핑을 "공변량 매핑"이라고합니다. 이것은 일련의 동물이 필요한 곳에 일련의 호랑이를 사용할 수 있지만 그 반대는 사실이 아닙니다. 일련의 호랑이가 필요한 곳에 일련의 동물을 반드시 사용할 수는 없습니다.

공분산입니다. 이제 모든 유형 세트의이 서브 세트를 고려하십시오.

{ IComparable<Tiger>, 
  IComparable<Animal>, 
  IComparable<Fruit>, 
  IComparable<Banana> }

이제 첫 번째 세트에서 세 번째 세트로의 맵핑이 있습니다 T → IC<T>.

C # 4에서 :

IC<Tiger>  ⇒ IC<Tiger>
IC<Animal> ⇒ IC<Tiger>     Backwards!
IC<Animal> ⇒ IC<Animal>
IC<Banana> ⇒ IC<Banana>
IC<Fruit>  ⇒ IC<Banana>     Backwards!
IC<Fruit>  ⇒ IC<Fruit>

즉, 매핑 T → IC<T>존재유지하지만 할당 호환성 방향반대로했습니다 . X ⇒ Y, 그렇다면 IC<X> ⇐ IC<Y>.

관계 유지하지만 반대로 하는 매핑을 반 변형 매핑 이라고합니다 .

Again, this should be clearly correct. A device which can compare two Animals can also compare two Tigers, but a device which can compare two Tigers cannot necessarily compare any two Animals.

So that's the difference between covariance and contravariance in C# 4. Covariance preserves the direction of assignability. Contravariance reverses it.


It's probably easiest to give examples - that's certainly how I remember them.

Covariance

Canonical examples: IEnumerable<out T>, Func<out T>

You can convert from IEnumerable<string> to IEnumerable<object>, or Func<string> to Func<object>. Values only come out from these objects.

It works because if you're only taking values out of the API, and it's going to return something specific (like string), you can treat that returned value as a more general type (like object).

Contravariance

Canonical examples: IComparer<in T>, Action<in T>

You can convert from IComparer<object> to IComparer<string>, or Action<object> to Action<string>; values only go into these objects.

This time it works because if the API is expecting something general (like object) you can give it something more specific (like string).

More generally

If you have an interface IFoo<T> it can be covariant in T (i.e. declare it as IFoo<out T> if T is only used in an output position (e.g. a return type) within the interface. It can be contravariant in T (i.e. IFoo<in T>) if T is only used in an input position (e.g. a parameter type).

It gets potentially confusing because "output position" isn't quite as simple as it sounds - a parameter of type Action<T> is still only using T in an output position - the contravariance of Action<T> turns it round, if you see what I mean. It's an "output" in that the values can pass from the implementation of the method towards the caller's code, just like a return value can. Usually this sort of thing doesn't come up, fortunately :)


I hope my post helps to get a language-agnostic view of the topic.

For our internal trainings I have worked with the wonderful book "Smalltalk, Objects and Design (Chamond Liu)" and I rephrased following examples.

What does “consistency” mean? The idea is to design type-safe type hierarchies with highly substitutable types. The key to get this consistency is sub type based conformance, if you work in a statically typed language. (We'll discuss the Liskov Substitution Principle (LSP) on a high level here.)

Practical examples (pseudo code/invalid in C#):

  • Covariance: Let's assume Birds that lay Eggs “consistently” with static typing: If the type Bird lays an Egg, wouldn't Bird's subtype lay a subtype of Egg? E.g. the type Duck lays a DuckEgg, then the consistency is given. Why is this consistent? Because in such an expression:Egg anEgg = aBird.Lay();the reference aBird could be legally substituted by a Bird or by a Duck instance. We say the return type is covariant to the type, in which Lay() is defined. A subtype's override may return a more specialized type. => “They deliver more.”

  • Contravariance: Let's assume Pianos that Pianists can play “consistently” with static typing: If a Pianist plays Piano, would she be able to play a GrandPiano? Wouldn't rather a Virtuoso play a GrandPiano? (Be warned; there is a twist!) This is inconsistent! Because in such an expression: aPiano.Play(aPianist); aPiano couldn't be legally substituted by a Piano or by a GrandPiano instance! A GrandPiano can only be played by a Virtuoso, Pianists are too general! GrandPianos must be playable by more general types, then the play is consistent. We say the parameter type is contravariant to the type, in which Play() is defined. A subtype's override may accept a more generalized type. => “They require less.”

Back to C#:
Because C# is basically a statically typed language, the "locations" of a type's interface that should be co- or contravariant (e.g. parameters and return types), must be marked explicitly to guarantee a consistent usage/development of that type, to make the LSP work fine. In dynamically typed languages LSP consistency is typically not a problem, in other words you could completely get rid of co- and contravariant "markup" on .Net interfaces and delegates, if you only used the type dynamic in your types. - But this is not the best solution in C# (you shouldn't use dynamic in public interfaces).

Back to theory:
The described conformance (covariant return types/contravariant parameter types) is the theoretical ideal (supported by the languages Emerald and POOL-1). Some oop languages (e.g. Eiffel) decided to apply another type of consistency, esp. also covariant parameter types, because it better describes the reality than the theoretical ideal. In statically typed languages the desired consistency must often be achieved by application of design patterns like “double dispatching” and “visitor”. Other languages provide so-called “multiple dispatch” or multi methods (this is basically selecting function overloads at run time, e.g. with CLOS) or get the desired effect by using dynamic typing.


The converter delegate helps me to understand the difference.

delegate TOutput Converter<in TInput, out TOutput>(TInput input);

TOutput represents covariance where a method returns a more specific type.

TInput represents contravariance where a method is passed a less specific type.

public class Dog { public string Name { get; set; } }
public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }

public static Poodle ConvertDogToPoodle(Dog dog)
{
    return new Poodle() { Name = dog.Name };
}

List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
poodles[0].DoBackflip();

참고URL : https://stackoverflow.com/questions/2184551/difference-between-covariance-contra-variance

반응형