태그 보관물: data-structures

data-structures

“unknown”과“missing”값을 어떻게 변수에 저장하고“unknown”과“missing”의 차이를 유지해야합니까? 만족스러운 해결책을 제시 할 수없는

이것을 “학문적”질문으로 생각하십시오. 나는 때때로 NULL을 피하는 것에 대해 궁금해하고 있으며 이것은 만족스러운 해결책을 제시 할 수없는 예입니다.


때때로 측정이 불가능하거나 누락 된 것으로 측정을 저장한다고 가정 해 봅시다. NULL을 피하면서 “빈”값을 변수에 저장하고 싶습니다. 다른 경우에는 값을 알 수 없습니다. 따라서 특정 시간 프레임에 대한 측정 값을 갖는 경우 해당 기간 내의 측정 값에 대한 쿼리는 3 가지 유형의 응답을 반환 할 수 있습니다.

  • 당시의 실제 측정 (예 :을 포함한 모든 숫자 값 0)
  • A “실종”/ “빈”값 (즉, 측정이 완료되었고, 값이됩니다 알려져 그 시점에서 비어있는).
  • 알 수없는 값 (즉, 해당 시점에서 측정이 수행되지 않았습니다. 비어있을 수 있지만 다른 값일 수도 있습니다).

중요한 설명 :

get_measurement()“빈”, “알 수 없음”및 “정수”유형의 값 중 하나를 리턴 하는 함수가 있다고 가정하십시오 . 숫자 값이 있으면 반환 값 (곱하기, 나누기 등)에서 특정 작업을 수행 할 수 있지만 NULL에서 이러한 작업을 사용하면 응용 프로그램이 중단되지 않으면 응용 프로그램이 중단됩니다.

예를 들어 (의사 코드)와 같은 NULL 검사를 피하면서 코드를 작성할 수 있기를 원합니다.

>>> value = get_measurement()  # returns `2`
>>> print(value * 2)
4

>>> value = get_measurement()  # returns `Empty()`
>>> print(value * 2)
Empty()

>>> value = get_measurement()  # returns `Unknown()`
>>> print(value * 2)
Unknown()

의 어느 것도 있습니다 print(더 널 (NULL)이 사용되지 않았다로) 문은 예외를 발생하지 않습니다. 따라서 비어 있고 알 수없는 값은 필요에 따라 전파되며 값이 실제로 “알 수 없음”인지 “비어 있는지”확인하는 것은 실제로 필요할 때까지 지연 될 수 있습니다 (어딘가에 값 저장 / 직렬화).


참고 : NULL을 피하고 싶은 이유는 주로 두뇌 맛보기입니다. 내가 일을 끝내고 싶다면 NULL을 사용하는 것에 반대하지 않지만, 그것들을 피하면 어떤 경우에는 코드를 훨씬 더 강력하게 만들 수 있다는 것을 알았습니다.



답변

적어도 기능적인 언어로 이것을하는 일반적인 방법은 차별적 인 노동 조합을 사용하는 것입니다. 그런 다음 유효한 int 중 하나 인 값, “missing”을 나타내는 값 또는 “unknown”을 나타내는 값입니다. F #에서는 다음과 같이 보일 수 있습니다.

type Measurement =
    | Reading of value : int
    | Missing
    | Unknown of value : RawData

Measurement값은 다음이 될 것이다 Readingint 값 또는 함께, Missing또는 Unknown같은 원시 데이터 value(필요한 경우).

그러나 차별적 노동 조합 또는 이에 상응하는 언어를 지원하는 언어를 사용하지 않는 경우이 패턴이 많이 사용되지 않을 수 있습니다. 따라서 예를 들어 열거 형 필드가있는 클래스를 사용하여 세 가지 중 올바른 데이터를 포함하는 클래스를 사용할 수 있습니다.


답변

모나드가 무엇인지 아직 모른다면 오늘 배우기에 좋은 날이 될 것입니다. 나는 여기에 OO 프로그래머를위한 부드러운 소개가있다 :

https://ericlippert.com/2013/02/21/monads-part-one/

귀하의 시나리오는 “아마도 모나드”에 대한 작은 확장 Nullable<T>이며 C # 및 Optional<T>기타 언어 로도 알려져 있습니다.

모나드를 나타내는 추상 유형이 있다고 가정 해 봅시다.

abstract class Measurement<T> { ... }

그리고 세 개의 서브 클래스 :

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

바인드 구현이 필요합니다.

abstract class Measurement<T>
{
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

이것으로부터이 간단한 바인드 버전을 작성할 수 있습니다 :

public Measurement<R> Bind(Func<A, R> f)
{
  return this.Bind(a => new Actual<R>(f(a));
}

이제 끝났습니다. 당신은 Measurement<int>손이 있습니다. 당신은 그것을 두 배로하고 싶습니다 :

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

그리고 논리를 따르십시오. 경우가 m있다 Empty<int>다음 asStringIS Empty<String>, 우수한.

마찬가지로, 우리가 가지고 있다면

Measurement<int> First()

Measurement<double> Second(int i);

그런 다음 두 가지 측정을 결합 할 수 있습니다.

Measurement<double> d = First().Bind(Second);

다시, 경우가 First()있다 Empty<int>다음 d이다 Empty<double>등등.

핵심 단계는 바인드 작업을 올바르게하는 것 입니다. 그것에 대해 열심히 생각하십시오.


답변

이 경우 Null Object Pattern의 변형이 유용 할 것이라고 생각합니다.

public class Measurement
{
    private int value;
    private bool isUnknown = false;
    private bool isMissing = false;

    private Measurement() { }
    public Measurement(int value) { this.value = value; }

    public int Value {
        get {
            if (!isUnknown && !isMissing)
            {
                return this.value;
            }
            throw new SomeException("...");
        }
    }

    public static readonly Measurement Unknown = new Measurement
    {
        isUnknown = true
    };

    public static readonly Measurement Missing = new Measurement
    {
        isMissing = true
    };
}

이를 구조체로 바꾸고, Equals / GetHashCode / ToString을 재정의하고 int, 또는에서 암시 적 변환을 추가 할 수 있으며, NaN과 같은 동작을 원한다면 자체 산술 연산자를 구현할 수도 있습니다. Measurement.Unknown * 2 == Measurement.Unknown.

즉, C # Nullable<int>은 모든 것을 구현하지만 다른 유형의 nulls를 구별 할 수 없다는 유일한 경고가 있습니다 . 나는 Java 사람이 아니지만 Java OptionalInt와 비슷한 언어이며 다른 언어에는 Optional유형 을 나타내는 자체 기능이있을 수 있습니다 .


답변

문자 그대로 정수를 사용해야한다면 가능한 해결책은 하나뿐입니다. 가능한 값 중 일부를 ‘누락’및 ‘알 수 없음’을 의미하는 ‘마법의 숫자’로 사용하십시오.

예 : 2,147,483,647 및 2,147,483,646

‘실제’측정을 위해 int가 필요한 경우 더 복잡한 데이터 구조를 만드십시오.

class Measurement {
    public bool IsEmpty;
    public bool IsKnown;
    public int Value {
        get {
            if(!IsEmpty && IsKnown) return _value;
            throw new Exception("NaN");
            }
        }
}

중요한 설명 :

클래스의 연산자를 오버로드하여 수학 요구 사항을 달성 할 수 있습니다.

public static Measurement operator+ (Measurement a, Measurement b) {
    if(a.IsEmpty) { return b; }
    ...etc
}


답변

변수가 부동 소수점 숫자 인 경우 IEEE754 (대부분의 최신 프로세서 및 언어에서 지원되는 부동 소수점 숫자 표준)는 잘 알려지지 않은 기능이지만 표준은 하나가 아니라 전체 제품군 을 정의합니다. 임의의 응용 프로그램 정의 의미에 사용할 수있는 NaN (숫자가 아님) 값입니다. 예를 들어 단 정밀도 부동 소수점에는 2 ^ {22} 유형의 유효하지 않은 값을 구별하는 데 사용할 수있는 22 개의 사용 가능한 비트가 있습니다.

일반적으로 프로그래밍 인터페이스는 인터페이스 중 하나만 노출합니다 (예 : Numpy ‘s nan). 명시 적 비트 조작 이외의 다른 방법을 생성하는 기본 제공 방법이 있는지 모르겠지만 몇 가지 하위 수준 루틴을 작성하는 것입니다. ( a == b하나의 NaN 인 경우 설계 상 항상 false를 반환 하기 때문에이를 구별 할 수있는 것도 필요 합니다.)

예를 들어, 당신은 당신이 사용하는 경우 발에서 자신을 촬영하는 위험하지 않습니다를 사용하여 올바르게 전파하기 때문에, 잘못된 데이터 신호를 무효 다움을 알리기 위해 자신의 “매직 넘버”개혁보다 더 나은 average()기능을하고 있는지 확인하는 것을 잊지 당신의 특별한 가치.

라이브러리가 애매 모호한 기능이기 때문에 라이브러리를 올바르게 지원하지 않는 유일한 위험은 nan다음과 같습니다.


답변

David Arno의 대답 에 따라 OOP의 차별적 인 노조와 Scala, Java 8 기능 유형 또는 Vavr 또는 Fugue 와 같은 Java FP 라이브러리와 같은 객체 기능 스타일로 공정하게 느낄 수 있습니다. 자연스럽게 다음과 같이 작성하십시오.

var value = Measurement.of(2);
out.println(value.map(x -> x * 2));

var empty = Measurement.empty();
out.println(empty.map(x -> x * 2));

var unknown = Measurement.unknown();
out.println(unknown.map(x -> x * 2));

인쇄

Value(4)
Empty()
Unknown()

( 요점으로 전체 구현 )

FP 언어 또는 라이브러리는 Try(aka Maybe) (값 또는 오류 Either를 포함하는 객체 ) 및 (성공 값 또는 실패 값을 포함하는 객체 )와 같은 다른 도구도 제공합니다 .


답변

알려진 문제와 알려진 신뢰할 수없는 측정의 차이점과 지원하려는 다운 스트림 프로세스의 차이점에 관심이있는 이유는 문제에 대한 이상적인 솔루션입니다. 이 경우 ‘다운 스트림 프로세스’에는 휴먼 오퍼레이터 또는 동료 개발자가 제외되지 않습니다.

단순히 “두 번째 풍미”의 널 (null)이 나오더라도 다운 스트림 프로세스 세트에 합리적인 동작 세트를 도출하기에 충분한 정보가 제공되지 않습니다.

다운 스트림 코드에서 발생하는 잘못된 동작의 소스에 대한 상황에 맞는 가정에 의존하는 경우,이를 나쁜 아키텍처라고합니다.

알려진 이유없이 실패 이유와 실패를 구별 할만큼 충분히 알고 있고 해당 정보가 미래의 행동에 영향을 줄 수있는 경우 해당 지식을 다운 스트림으로 전달하거나 인라인으로 처리해야합니다.

이것을 처리하기위한 몇 가지 패턴 :

  • 합계 유형
  • 차별적 노동 조합
  • 연산 결과를 나타내는 열거 형과 결과 필드를 포함하는 객체 또는 구조체
  • 정상적인 작동으로는 달성 할 수없는 매직 스트링 또는 매직 넘버
  • 이 사용이 관용적 인 언어의 예외
  • 이 두 시나리오를 구별하고 실제로 사용하는 데 아무런 가치가 없다는 것을 깨닫습니다. null