측정 단위에 액세스하기위한 데이터 구조


17

TL; DR 측정 단위 내에서 단위를 정의하기위한 최적의 데이터 구조를 설계하려고합니다.


A Unit of measure는 본질적 value으로unit 입니다. SI 단위 에는 7 개의베이스 또는 치수가 있습니다. 즉, 길이, 질량, 시간, 전류, 온도, 물질의 양 (몰) 및 광도.

이것은 간단하지만 많은 파생 단위와 자주 사용하는 요금이 있습니다. 결합 된 단위의 예는 다음과 같습니다.kg * m / s^2 이고 비율의 예는입니다 tons / hr.

묵시적 유닛에 크게 의존하는 응용 프로그램이 있습니다. 변수 또는 열 이름에 단위를 포함시킵니다. 그러나 다른 단위로 측정 단위를 지정해야 할 때 문제가 발생합니다. 예, 입력 및 표시시 값을 변환 할 수 있지만 이로 인해 자체 클래스 내에서 캡슐화하려는 많은 오버 헤드 코드가 생성됩니다.

코드 플렉스 및 기타 공동 작업 환경에는 여러 가지 솔루션이 있습니다. 프로젝트 라이센스는 합리적이지만 프로젝트 자체는 대개 너무 가볍거나 무겁습니다. 우리는 "바로"유니콘을 쫓고 있습니다.

이상적으로는 다음과 같이 새 측정 단위를 정의 할 수 있습니다.

UOM myUom1 = 새로운 UOM (10, 볼트);
UOM myUom2 = 새로운 UOM (43.2, 뉴턴);

물론 고객의 요구에 따라 Imperial과 SI를 혼합하여 사용합니다.

또한이 단위 구조를 향후 데이터베이스 테이블과 동기화하여 데이터 내에서 동일한 수준의 일관성을 제공 할 수 있어야합니다.


측정 단위 클래스를 만드는 데 사용해야하는 단위, 파생 단위 및 비율을 정의하는 가장 좋은 방법은 무엇입니까? 하나 이상의 열거 형을 사용하는 것을 볼 수 있지만 다른 개발자에게는 실망 할 수 있습니다. 단일 열거 형은 200 개 이상의 항목으로 구성되는 반면, 여러 대의 열거 형은 SI 대 영국식 단위를 기반으로 혼동 될 수 있고 단위 자체의 분류를 기반으로 한 추가 분류가 혼동 될 수 있습니다.

내 관심사 중 일부를 보여주는 열거 형 예 :

myUnits.Volt
myUnits.Newton
myUnits.meter

SIUnit.meter
ImpUnit.foot DrvdUnit.Newton
DrvdUnitSI.Newton
DrvdUnitImp.FtLbs

사용중인 단위는 꽤 잘 정의되어 있으며 유한 공간입니다. 고객이 요구할 때 새로운 파생 단위 나 요율을 확장하고 추가 할 수있는 기능이 필요합니다. 더 넓은 디자인 측면이 여러 언어에 적용 가능하다고 생각하지만 프로젝트는 C #입니다.


내가 본 라이브러리 중 하나는 문자열을 통해 단위의 자유 형식 입력을 허용합니다. 그런 다음 UOM 클래스는 문자열을 구문 분석하고 그에 따라 슬롯을 지정했습니다. 이 접근 방식의 문제점은 개발자가 올바른 문자열 형식을 생각하고 기억해야한다는 것입니다. 그리고 생성자에서 전달되는 문자열의 유효성을 검사하기 위해 코드 내에 추가 검사를 추가하지 않으면 런타임 오류 / 예외가 발생할 위험이 있습니다.

다른 라이브러리는 본질적으로 개발자가 작업해야 할 클래스를 너무 많이 만들었습니다. 동등한 UOM와 함께 그것은 제공 DerivedUnitRateUnit등등. 본질적으로 코드는 우리가 해결하는 문제에 대해 지나치게 복잡했습니다. 이 라이브러리는 본질적으로 모든 조합을 허용하지만 (단위 세계에서 합법적 인) 모든 조합을 허용하지 않아 문제의 범위를 좁 힙니다 (코드 단순화).

다른 라이브러리는 엄청나게 간단했으며 예를 들어 연산자 오버로드를 고려하지 않았습니다.

또한 잘못된 변환 (예 : 볼트-미터) 시도에 대해 걱정하지 않습니다. 이 시점에서이 레벨에서 액세스 할 수있는 것은 개발자뿐입니다. 따라서 이러한 유형의 실수로부터 보호 할 필요는 없습니다.


찾은 라이브러리가 필요에 맞지 않는 방법을 설명해 주시겠습니까?
svick


1
@MainMa-해당 링크에 감사드립니다. 문제 공간이 크기 때문에 허용 된 변환 만 선언 할 수 있으므로 차원 분석을 수행 할 필요가 없습니다. 생성하는 슬로 그가 될 것이지만 한 번의 비용입니다.

1
어떤 종류의 전환이 필요한지 설명 할 수 있습니까? 스케일링 변환 (예 : 미터에서 센티미터) 또는 교차 차원 변환 (예 : 질량 대 힘)입니까?
Bart van Ingen Schenau

1
코드의 일부를 F #으로 옮기는 것을 고려 했습니까? 이 언어에는 측정 단위 빌드 int가 있습니다.
Pete

답변:


11

C ++ 용 Boost 라이브러리에는 측정 단위 처리의 샘플 구현을 제시하는 차원 분석 관련 기사가 포함되어 있습니다.

요약 : 측정 단위는 벡터로 표현되며 벡터의 각 요소는 기본 치수를 나타냅니다.

typedef int dimension[7]; // m  l  t  ...
dimension const mass      = {1, 0, 0, 0, 0, 0, 0};
dimension const length    = {0, 1, 0, 0, 0, 0, 0};
dimension const time      = {0, 0, 1, 0, 0, 0, 0};

파생 단위는 이들의 조합입니다. 예를 들어 힘 (질량 * 거리 / 시간 ^ 2)은 다음과 같이 표시됩니다.

dimension const force  = {1, 1, -2, 0, 0, 0, 0};

변환 계수를 추가하여 영국식 대 SI 단위를 처리 할 수 ​​있습니다.

이 구현은 템플릿 메타 프로그래밍을 사용하여 다양한 측정 단위를 다른 컴파일 타임 유형으로 쉽게 전환하는 C ++ 관련 기술에 의존하지만 개념은 다른 프로그래밍 언어로 이전해야합니다.


그래서 파생 된 모든 단위가 C ++ const와 동일합니까? 나는 그들이 오염 물질을 피하기 위해 네임 스페이스에 싸여 있다고 가정합니까?

1
@ GlenH7-템플릿 메타 프로그래밍 작업에 들어갑니다. 그것들은 실제로 mpl::vector_c<int,1,0,0,0,0,0,0>const 대신 별도의 유형 (예 :)으로 표현됩니다. 이 기사는 설명을 통해 consts 접근 방식을 먼저 제시합니다 (아마도 잘 설명하지 못했습니다). const를 사용하면 대안으로 작동합니다 (컴파일 타임 유형 안전이 손실 될 수 있습니다). 이름 오염을 피하기 위해 네임 스페이스를 사용하는 것은 확실히 옵션입니다.
Josh Kelley

8

방금 GithubNuGet 에서 Units.NET을 릴리스했습니다 .

모든 공통 단위와 변환을 제공합니다. 가볍고 단위 테스트되었으며 PCL을 지원합니다.

당신의 질문으로 :

  • 이것은 구현의 더 가벼운 끝에 있습니다. 초점은 측정 단위의 명확한 표현, 변환 및 구성을 돕는 것입니다.
  • 방정식 솔버가 없으며 계산에서 새 단위를 자동으로 도출하지 않습니다.
  • 단위를 정의하기위한 하나의 큰 열거 형.
  • 단위 간 동적 변환을위한 UnitConverter 클래스
  • 단위간에 명시 적으로 변환하기위한 변경 불가능한 데이터 구조
  • 간단한 산술을위한 과부하 연산자.
  • 새 단위 및 변환으로 확장하는 것은 동적 변환을위한 새로운 열거 형을 추가하고 명시 적 변환 속성 및 연산자 오버로드를 정의하기 위해 길이와 같은 측정 단위를 추가하는 것입니다.

나는이 영역에서 해결책의 성배를 아직 보지 못했다. 당신이 말하면, 그것은 너무 복잡하거나 장황하게 일하기가 쉽지 않습니다. 때때로, 일을 단순하게 유지하는 것이 가장 좋으며, 나의 필요에 따라이 접근법은 충분히 입증되었습니다.

명시 적 변환

Length meter = Length.FromMeters(1);
double cm = meter.Centimeters; // 100
double yards = meter.Yards; // 1.09361
double feet = meter.Feet; // 3.28084
double inches = meter.Inches; // 39.3701

Pressure p = Pressure.FromPascal(1);
double kpa = p.KiloPascals; // 1000
double bar = p.Bars; // 1 × 10-5
double atm = p.Atmosphere; // 9.86923267 × 10-6
double psi = p.Psi; // 1.45037738 × 10-4

동적 변환

// Explicitly
double m = UnitConverter.Convert(1, Unit.Kilometer, Unit.Meter); // 1000
double mi = UnitConverter.Convert(1, Unit.Kilometer, Unit.Mile); // 0.621371
double yds = UnitConverter.Convert(1, Unit.Meter, Unit.Yard); // 1.09361

// Or implicitly.
UnitValue val = GetUnknownValueAndUnit();

// Returns false if conversion was not possible.
double cm;
val.TryConvert(LengthUnit.Centimeter, out cm);

귀하의 예는 본질적으로Truple<T1, T2, T3>(x, y, z)
Chef_Code

무슨 뜻인지 모르겠지만 각 단위에 대해 하나의 값만 저장되어 있습니다. 길이의 경우 double 유형의 미터 필드를 보유하고 질량의 경우 킬로그램입니다. 다른 단위로 변환 할 때 변환 기능을 통해 해당 값을 실행합니다. 이 샘플은 현재 약간 구식이지만 동일한 개념이 적용됩니다.
angularsen

내가 아니라 결론에 뛰어으로 ... 내가 의미 잘못 말한 것 같아요 Tuple. UnitConverter수업을 볼 수 없지만 IMO는 Tuple수업 과 유사한 기능을 공유하는 것으로 보입니다 .
Chef_Code

여전히 Tuple 비교에 대해서는 확실하지 않지만 사용법에 대한 업데이트 된 예제 는 github 페이지 를 참조하십시오 .
angularsen

3

C # 대신 F #으로 전환 할 수 있다면 F #에는 수행하려는 작업에 맞는 것처럼 보이는 측정 단위 (값에 메타 데이터를 사용하여 구현)가 있습니다.

http://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure

특히 :

// Additionally, we can define types measures which are derived from existing measures as well:

[<Measure>] type m                  (* meter *)
[<Measure>] type s                  (* second *)
[<Measure>] type kg                 (* kilogram *)
[<Measure>] type N = (kg * m)/(s^2) (* Newtons *)
[<Measure>] type Pa = N/(m^2)       (* Pascals *)

좋은 제안이며 우리는 그것을 고려했습니다. F #을 사용하면 장치가 출력에 표시되는 방식을 제어 할 수 있다고 생각하지 않습니다.

2
@ GlenH7 당신이 옳다고 생각합니다 :Important: Units of measure look like a data type, but they aren't. .NET's type system does not support the behaviors that units of measure have, such as being able to square, divide, or raise datatypes to powers. This functionality is provided by the F# static type checker at compile time, **but units are erased from compiled code**. Consequently, it is not possible to determine value's unit at runtime.
paul

3

모든 필요한 변환이 스케일링 변환이라는 사실에 근거하여 (온도 변환을 지원해야하는 경우를 제외하고, 변환이 오프셋을 포함하는 계산은 훨씬 더 복잡한 계산), 다음과 같이 '측정 단위'시스템을 설계합니다.

  • unit스케일링 계수, 단위 텍스트 표현을위한 문자열 및 스케일링되는 참조를 포함하는 클래스 unit입니다. 텍스트 표현은 표시 목적과 기본 단위에 대한 참조로 다른 단위의 값에 대해 수학을 수행 할 때 결과가 어떤 단위인지 알 수 있습니다.

    지원되는 각 단위에 대해 unit클래스 의 정적 인스턴스 가 제공됩니다.

  • UOM값과 값에 대한 참조를 포함하는 클래스 unit입니다. 이 UOM클래스는 다른 것을 더하거나 빼고 UOM차원없는 값으로 곱하기 / 나누기 위해 오버로드 된 연산자를 제공 합니다.

    더하기 / 빼기가 UOM같은 두 개 에 대해 unit수행되면 직접 수행됩니다. 그렇지 않으면 두 값이 각각의 기본 단위로 변환되고 가감 산됩니다. 결과는 기본에있는 것으로보고됩니다 unit.

사용법은

unit volts = new unit(1, "V"); // base-unit is self
unit Newtons = new unit(1, "N"); // base-unit is self
unit kiloNewtons = new unit(1000, "kN", Newtons);
//...
UOM myUom1 = new UOM(10, volts);
UOM myUom2 = new UOM(43.2, kiloNewtons);

호환되지 않는 장치에 대한 작업은 문제로 간주되지 않으므로 디자인을 안전하게 형식화하려고 시도하지 않았습니다. 두 장치가 동일한 기본 장치를 나타내는 지 확인하여 런타임 검사를 추가 할 수 있습니다.


온도를 언급 한 이후 : 무엇 95F - 85F입니까? 무엇입니까 20C - 15C? 두 예에서 모두 두 UOM개가 동일 unit합니다. 빼기가 직접 수행됩니까?

@MattFenwick : 결과는 각각 10 F5 C입니다. 불필요한 변환을 피하기 위해 가능하면 계산이 직접 수행됩니다. 단위 변환 방법을에 추가하는 것은 매우 사소한 UOM일이지만 Celsius-Fahrenheit 변환 unit의 경우 스케일링 계수 외에 오프셋 가능성으로 클래스를 확장해야합니다.
Bart van Ingen Schenau

그러나 95F - 85F! = 10F.

1
@MattFenwick : 저를 계몽하십시오. 당신의 온도를 낮출 경우 어떻게 추위가 나올까요 95F으로 85F? 내 지식으로는 화씨는 여전히 선형 척도입니다.
Bart van Ingen Schenau

2
이 켈빈로 변환하기 쉽기 때문의이 섭씨 예를하자 : 우리가 말한다면 20C - 15C = 5C, 우리가 말을하는지 293.15K - 288.15K = 278.15K분명히 잘못이다.

2

코드가 수행하는 작업과 허용되는 작업을 생각하십시오. 가능한 모든 단위가 포함 된 간단한 열거 형을 사용하면 전압을 미터로 변환하는 것과 같은 작업을 수행 할 수 있습니다. 그것은 분명히 인간에게는 유효하지 않지만 소프트웨어는 기꺼이 시도 할 것입니다.

나는 이것과 한 번 모호한 일을했고, 구현에는 모두 구현 된 추상 기본 클래스 (길이, 무게 등)가 IUnitOfMeasure있었습니다. 각 추상 기본 클래스 LengthMeter모든 변환 작업에 사용할 기본 유형 (클래스 의 기본 구현 클래스가 있음 )을 정의했습니다. 따라서 IUnitOfMeasure두 가지 다른 방법을 구현 ToDefault(decimal)했으며FromDefault(decimal) .

랩핑하려는 실제 숫자 IUnitOfMeasure는 일반 인수로 허용되는 일반 유형이었습니다 . 같은 것을 말하면 Measurement<Meter>(2.0)자동 타입 안전을 제공합니다. 이러한 클래스에서 올바른 암시 적 변환 및 수학 메서드를 구현하면 Measurement<Meter>(2.0) * Measurement<Inch>(12)기본 유형 ( Meter) 으로 결과를 반환하고 결과를 반환 할 수 있습니다 . 나는 뉴턴과 같은 파생 유닛을 결코 운동하지 않았다. 나는 단순히 킬로그램 * 미터 / 초 / 초로 남겨 두었습니다.


제네릭 형식을 사용하여 제안하는 접근법이 마음에 듭니다.

1

그 대답은 MarioVW의 스택 오버플로 응답 에 있다고 생각합니다 .

실용 예제 Tuple을 .Net 4-0에서 사용할 수있는 곳은?

튜플을 사용하면 2 차원 사전 (또는 그 문제에 대해 n 차원)을 쉽게 구현할 수 있습니다. 예를 들어, 이러한 사전을 사용하여 환전 맵핑을 구현할 수 있습니다.

var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);
decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58

신청서도 비슷하게 필요했습니다. Tuple무게와 측정과 같은 물체에도 적용 할 수있는 불변성입니다.


0

내 프로토 타입 코드 : http://ideone.com/x7hz7i

내 디자인 포인트 :

  1. 속성 가져 오기 / 설정으로 UoM (측정 단위) 선택
    길이 len = 새 길이 ();
    len.Meters = 2.0;
    Console.WriteLine (len.Feet);
    
  2. UoM 선택을위한 명명 된 생성자
    길이 len = Length.FromMeters (2.0);
    
  3. UoM에 대한 ToString 지원
    Console.WriteLine (len.ToString ( "ft"));
    Console.WriteLine (len.ToString ( "F15"));
    Console.WriteLine (len.ToString ( "ftF15"));
    
  4. 왕복 변환 (배정 밀도로 무시할 수없는 반올림 손실)
    길이 lenRT = Length.FromMeters (Length.FromFeet (Length.FromMeters (len.Meters) .Feet) .Meters) ;;
    
  5. 연산자 과부하 ( 치수 유형 검사 부족 )
    // 매우 지저분하고 버그가 많으며 안전하지 않으며 F # 또는 C ++ MPL을 사용하지 않으면 불가능할 수 있습니다.
    // 차원 분석선택적 기능아니라고 계속 말합니다. UoM
    // 직접 사용하는지 여부 그것은됩니다 필요합니다 .
    

0

불행히도 독창적 인 잡지에는 좋은 기사가 있습니다 : http://www.dotnetpro.de/articles/onlinearticle1398.aspx

기본 아이디어는 BaseMeasurement가있는 Length와 같은 Unit 클래스를 갖는 것입니다. 이 클래스에는 변환 팩터, 연산자 오버로드, ToString 오버로드, 문자열 파서 및 인덱서 구현이 포함됩니다. 심지어 Architectual view도 구현했지만 라이브러리로 릴리스되지 않았습니다.

public class Length : MeasurementBase
    {
        protected static double[] LengthFactors = { 1, 100, 1000, 0.001, 100 / 2.54 };
        protected static string[] LengthSymbols = { "m", "cm", "mm", "km", "in" };
...
      public virtual double this[Units unit]
        {
            get { return BaseValue * LengthFactors[(int)unit]; }
            set { BaseValue = value / LengthFactors[(int)unit]; }
        }
...

        public static ForceDividedByLength operator *(Length length, Pressure pressure1)
        {
            return new ForceDividedByLength(pressure1[Pressure.Units.kNm2] * length[Units.m], ForceDividedByLength.Units.kNm);
        }

...

따라서 압력 연산자 또는 다음과 같은 사용법을 볼 수 있습니다.

var l = new Length(5, Length.Units.m)    
Area a = l * new Length("5 m");
a.ToString() // -> 25 m^2
double l2 = l[Length.Units.ft];

그러나 당신이 말했듯이, 나는 유니콘도 찾지 못했습니다 :)


-1

이것은 유닉스 명령의 핵심 요소 이며 units, 관계를 지정하기 위해 데이터 파일 중심의 접근 방식을 사용합니다.


언급 해 주셔서 감사합니다 units. 더 넓은 솔루션에서 단위가 작동하지 않는 주된 이유는 자유 형식 문자열입니다. 물론, 오류 메시지를 반환하지만이 방법은이 코드를 애플리케이션과 통합 할 개발자를 대상으로합니다. 자유 형식 문자열은 오류가 발생할 기회가 너무 많습니다.

1
units의 데이터 파일을 살펴 봐야 합니다. 수량 간의 관계를 정의하는 방식은 매우 깨끗하며 문제에 유용 할 수 있습니다.
로스 패터슨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.