이기종 목록에 대한 특정 목적이 있습니까?


13

C #과 Java 배경에서 왔을 때, 나는 목록이 균질하다는 것에 익숙합니다. Lisp를 집어 들었을 때 목록이 이기종 일 수 있음을 알았습니다. dynamicC # 에서 키워드로 문제를 해결하기 시작했을 때 C # 4.0부터 이기종 목록이있을 수 있음을 알았습니다.

List<dynamic> heterogeneousList

내 질문은 요점이 뭐야? 이기종 목록은 처리를 수행 할 때 훨씬 많은 오버 헤드가있는 것처럼 보이며 한 장소에 다른 유형을 저장 해야하는 경우 다른 데이터 구조가 필요할 수 있습니다. 나의 순진함이 추악한 얼굴을 키우고 있습니까? 아니면 이기종 목록을 갖는 것이 유용한 때가 있습니까?


1
... 목록이이기 종일 수 있다는 것을 알았습니까?
세계 엔지니어

List<dynamic>(질문과 관련하여) 단순히하는 것과 어떻게 다른 List<object>가요?
Peter K.

@WorldEngineer 네, 그렇습니다. 내 게시물을 업데이트했습니다. 감사!
Jetti

@PeterK. 나는 매일 사용하기 위해 아무런 차이가 없다고 생각합니다. 그러나 C #의 모든 유형이 System.Object에서 파생되는 것은 아니므로 차이점이있는 경우가 있습니다.
Jetti

답변:


16

이 신문은 강하게 이기종 컬렉션 형식화 된 올렉 Kiselyov에 의해, 랄프 Lämmel, 그리고 Keean Schupke 하스켈 이종리스트의 구현뿐만 아니라, 언제, 왜 당신이 HLists을 사용 방법에 대한 동기를 부여하는 예뿐만 아니라 포함되어 있습니다. 특히 형식 안전 컴파일 타임 검사 데이터베이스 액세스에 사용하고 있습니다. (LINQ는 사실, 그들은 참조하는 용지가 생각 입니다 LINQ되었다 에릭 마이어 등으로 하스켈 종이.)

HLists 논문의 소개 단락에서 인용 :

이기종 콜렉션을 요구하는 일반적인 예제의 개방형 목록은 다음과 같습니다.

  • 다른 유형의 항목을 저장해야하는 기호 테이블은 이기종입니다. 결과 유형이 인수 값에 따라 결정되는 유한 맵입니다.
  • XML 요소는 이기종 형식입니다. 실제로 XML 요소는 정규식과 1- 모호성 속성으로 제한되는 중첩 컬렉션입니다.
  • SQL 조회에 의해 리턴되는 각 행은 열 이름에서 셀로 이기종 맵입니다. 쿼리 결과는 이종 행의 동종 스트림입니다.
  • 기능적 언어에 고급 개체 시스템을 추가하려면 확장 가능한 레코드와 하위 유형 및 열거 인터페이스를 결합하는 이종 종류의 컬렉션이 필요합니다.

귀하의 질문에 제시 한 예는 실제로 단어가 일반적으로 사용된다는 의미에서 이기종 목록이 아닙니다. 그것들은 약한 유형 이거나 유형이 지정되지 않은 목록입니다. 사실, 그들은 실제로 균일 한 모든 요소가 같은 유형이기 때문에, 목록 : objectdynamic. 그런 다음 instanceof실제로 요소에 대해 의미있게 작업 할 수 있도록 캐스트 또는 검사되지 않은 테스트 등 을 수행해야합니다 .


링크와 답변에 감사드립니다. 목록이 실제로 이기종이 아니라 약한 유형이라는 점을 잘 알고 있습니다. 나는 그 논문을 기대하고있다. (아마 내일, 오늘 밤 중간에
데려다 주었을 것이다

5

짧고 이기종 컨테이너는 유연성을 위해 런타임 성능을 교환합니다. 특정 유형의 물건에 관계없이“물건 목록”을 원한다면 이질성이 있습니다. 리스프는 특성상 동적으로 유형이 지정되며 대부분의 경우 상자 값의 단점 목록이므로 성능 저하가 적을 것으로 예상됩니다. Lisp 세계에서 프로그래머 생산성은 런타임 성능보다 더 중요합니다.

동적으로 유형이 지정된 언어에서 동종 컨테이너는 이종 컨테이너에 비해 실제로 약간의 오버 헤드가 발생합니다. 추가 된 모든 요소는 유형 검사가 필요하기 때문입니다.

더 나은 데이터 구조를 선택하는 것에 대한 직감이 적절합니다. 일반적으로 코드에 더 많은 계약을할수록 코드 작동 방식에 대해 더 많이 알 수 있으며 더 안정적이고 유지 관리가 용이합니다. 됩니다. 그러나 때때로 당신은 정말로 이종 컨테이너를 원하고, 당신은 당신이 그것을 필요로하는 경우에 하나를 가질 수되어야한다.


1
"그러나 때로는 이기종 컨테이너를 원하기 때문에 필요한 경우 컨테이너를 가져야합니다." 왜 그래? 그게 내 질문입니다. 왜 많은 데이터를 임의의 목록에 넣을 필요가 있습니까?
Jetti

@Jetti : 다양한 유형의 사용자 입력 설정 목록이 있다고 가정합니다. 인터페이스를 만들어 IUserSetting여러 번 구현하거나 generic을 만들 수 UserSetting<T>있지만 정적 유형 지정의 문제점 중 하나는 인터페이스 사용법을 정확하게 알고 있기 전에 인터페이스를 정의하고 있다는 것입니다. 정수 설정으로 수행하는 작업은 문자열 설정으로 수행하는 작업과 매우 다를 수 있으므로 일반적인 인터페이스에 어떤 작업이 적합한가요? 확실하게 알 때까지 동적 타이핑을 신중하게 사용하고 나중에 구체적으로 만드는 것이 좋습니다.
Jon Purdy

내가 문제를 겪는 곳을 참조하십시오. 나에게 그것은 나쁜 디자인처럼 보이며, 무엇을하고 / 사용할지 알기 전에 무언가를 만든다. 또한이 경우 객체 반환 값으로 인터페이스를 만들 수 있습니다. 이기종 목록과 동일한 기능을 수행하지만 인터페이스에 사용 된 유형이 무엇인지 알고 있으면보다 명확하고 쉽게 수정할 수 있습니다.
Jetti

@Jetti : 기본적으로 동일한 문제입니다. 어떤 기본 동작이 정의 되든 관계없이 이러한 작업이 의미가없는 유형이기 때문에 유니버설 기본 클래스는 처음에는 존재하지 않아야합니다. 그러나 C #에서 objecta보다 사용하기 가 더 쉽다면 dynamic, 전자를 사용하십시오.
Jon Purdy

1
@Jetti : 이것이 다형성에 관한 것입니다. 단일 수퍼 클래스의 서브 클래스 일 수 있지만 목록에는 많은 "이종성"오브젝트가 포함됩니다. Java 관점에서 클래스 (또는 인터페이스) 정의를 올바르게 얻을 수 있습니다. 다른 언어 (LISP, Python 등)의 경우 실제 구현 차이가 없기 때문에 모든 선언을 올바르게 얻는 데 이점이 없습니다.
S.Lott

2

기능 언어 (예 : lisp)에서는 패턴 일치를 사용하여 목록의 특정 요소에 어떤 일이 발생하는지 확인합니다. C #에서 동등한 것은 요소의 유형을 확인하고이를 기반으로 작업을 수행하는 if ... elseif 문의 체인입니다. 말할 필요도없이, 기능 패턴 일치는 런타임 유형 검사보다 효율적입니다.

다형성을 사용하는 것이 패턴 일치에 더 가깝습니다. 즉, 목록의 객체가 특정 인터페이스와 일치하도록하고 각 객체에 대해 해당 인터페이스에서 함수를 호출합니다. 또 다른 대안은 특정 객체 유형을 매개 변수로 취하는 일련의 오버로드 된 메소드를 제공하는 것입니다. Object를 매개 변수로 사용하는 기본 방법입니다.

public class ListVisitor
{
  public void DoSomething(IEnumerable<dynamic> list)
  {
    foreach(dynamic obj in list)
    {
       DoSomething(obj);
    }
  }

  public void DoSomething(SomeClass obj)
  {
    //do something with SomeClass
  }

  public void DoSomething(AnotherClass obj)
  {
    //do something with AnotherClass
  }

  public void DoSomething(Object obj)
  {
    //do something with everything els
  }
}

이 접근 방식은 Lisp 패턴 일치에 대한 근사치를 제공합니다. 방문자 패턴 (여기에서 구현 된 것처럼 이기종 목록 사용의 좋은 예) 다른 예는 우선 순위 큐에 특정 메시지에 대한 리스너가 있고 책임 체인을 사용하는 메시지 디스패치의 경우, 디스패처는 메시지를 전달하고 메시지와 일치하는 첫 번째 핸들러는 메시지를 처리합니다.

반대쪽은 메시지를 등록하는 모든 사람에게 알리는 것입니다 (예 : MVVM 패턴에서 ViewModel의 느슨한 결합에 일반적으로 사용되는 이벤트 집 계기 패턴). 다음 구성을 사용합니다

IDictionary<Type, List<Object>>

사전에 추가하는 유일한 방법은 함수입니다

Register<T>(Action<T> handler)

(그리고 객체는 실제로 전달 된 핸들러에 대한 WeakReference입니다). 그래서 컴파일 타임에 닫힌 유형이 무엇인지 모르기 때문에 List <Object>를 사용해야합니다. 그러나 런타임에 사전의 키가되는 유형이되도록 강제 할 수 있습니다. 이벤트를 시작하고 싶을 때

Send<T>(T message)

다시 목록을 확인합니다. 어쨌든 캐스트해야하기 때문에 List <dynamic>을 사용하면 이점이 없습니다. 보시다시피 두 접근 방식에는 장점이 있습니다. Method overloading을 사용하여 객체를 동적으로 디스패치하려는 경우 dynamic이 수행 방법입니다. 관계없이 캐스팅을 강요 받았다면 Object를 사용할 수도 있습니다.


패턴 일치를 사용하면 일치하는 데이터 유형의 선언에 따라 사례가 (보통 ML 및 Haskell에서) 설정됩니다. 이러한 유형을 포함하는 목록도 이기종이 아닙니다.

ML과 Haskell에 대해서는 잘 모르겠지만 Erlang은 어떤 항목과도 ​​일치 할 수 있습니다. 다른 일치 항목이 없기 때문에 여기에 도달하면이 작업을 수행하십시오.
Michael Brown

@MikeBrown-이것은 훌륭하지만 왜 이기종 목록을 사용하고 항상 List <dynamic>과 함께 작동하지는 않는지 설명하지 않습니다.
Jetti

4
C #에서 오버로드는 컴파일 타임에 해결됩니다 . 따라서 예제 코드는 항상 호출합니다 DoSomething(Object)(적어도 루프 object에서 사용할 때는 완전히 다른 것입니다). foreachdynamic
Heinzi

@Heinzi, 당신 말이 맞아요 ... 오늘 졸려요 : P fixed
Michael Brown

0

이기종이 런타임 오버 헤드를 발생시키는 것이 맞지만 유형 검사기에서 제공하는 컴파일 타임 보장을 약화시킵니다. 그럼에도 불구하고 대안이 훨씬 더 비싼 문제가 있습니다.

내 경험상 파일, 네트워크 소켓 등을 통해 원시 바이트를 처리하면 종종 이러한 문제가 발생합니다.

실제 사례를 제시하려면 선물을 사용한 분산 계산 시스템을 고려하십시오 . 개별 노드의 작업자는 직렬화 가능한 모든 유형의 작업을 생성하여 해당 유형의 미래를 산출 할 수 있습니다. 무대 뒤에서 시스템은 작업을 동료에게 보낸 다음 해당 작업 단위가 해당 작업에 대한 답변이 반환되면 채워 져야하는 특정 미래와 연결하는 레코드를 저장합니다.

이 기록들은 어디에 보관할 수 있습니까? 직관적으로, 당신이 원하는 것은과 Dictionary<WorkId, Future<TValue>>같지만 이것은 전체 시스템에서 한 가지 유형의 미래 만 관리하도록 제한합니다. Dictionary<WorkId, Future<dynamic>>근로자가 미래를 강요 할 때 적절한 유형으로 캐스팅 할 수 있기 때문에 더 적합한 유형은 입니다.

참고 :이 예는 하위 유형이없는 Haskell 세계에서 나왔습니다. C # 에서이 특정 예제에 대한 관용적 솔루션이 있는지는 놀랄 일이 아니지만 여전히 잘 설명되어 있습니다.


0

ISTR은 리스프는 집계 데이터 객체의 모든 유형을 필요로하는 경우 그래서, 것, 목록 이외의 데이터 구조를 가지고 있지 않는 이종 목록이 될 수 있습니다. 다른 사람들이 지적했듯이 전송 또는 저장을 위해 데이터를 직렬화하는 데 유용합니다. 좋은 기능 중 하나는 개방형이므로 파이프 및 필터 유추를 기반으로 한 시스템에서이를 사용할 수 있으며 고정 된 데이터 개체 또는 워크 플로 토폴로지 없이도 처리 단계를 늘리거나 데이터를 수정하는 연속적인 처리 단계를 가질 수 있다는 것입니다. .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.