Ölçü birimlerine erişmek için veri yapısı


17

TL; DR - Bir ölçü birimi içindeki birimleri tanımlamak için en uygun veri yapısını tasarlamaya çalışıyorum.


A Unit of measureesasen a valueile ilişkili bir (veya miktar) ' dır unit. SI Üniteleri yedi tabana veya boyuta sahiptir. Yani: uzunluk, kütle, zaman, elektrik akımı, sıcaklık, madde miktarı (mol) ve ışık şiddeti.

Bu yeterince basit olacaktır, ancak sık kullandığımız oranların yanı sıra bir dizi türetilmiş birim de vardır. Örnek birleşik birim Newton: kg * m / s^2örnek bir oran olacaktır tons / hr.

Zımni birimlere dayanan bir uygulamamız var. Birimleri değişken veya sütun adı içine gömeceğiz. Ancak bu, farklı birimlerle bir ölçü birimi belirtmemiz gerektiğinde sorun yaratır. Evet, değerleri giriş ve görüntüde dönüştürebiliriz, ancak bu, kendi sınıfında kapsüllemek istediğimiz çok fazla genel kod oluşturur.

Codeplex ve diğer ortak çalışma ortamlarında bir takım çözümler var. Projelerin lisanslanması kabul edilebilir ancak projenin kendisi genellikle çok hafif veya çok ağır olur. Kendi "tek doğru" unicorn'umuzu kovalıyoruz.

İdeal olarak, böyle bir şey kullanarak yeni bir ölçü birimi tanımlayabilirim:

UOM myUom1 = yeni UOM (10, volt);
UOM myUom2 = yeni UOM (43.2, Newton);

Elbette, müşterilerimizin ihtiyaçlarına göre bir dizi Imperial ve SI birimi kullanıyoruz.

Verilerimizde de aynı tutarlılık derecesini sağlayabilmemiz için, bu birimler yapısını gelecekteki bir veritabanı tablosuyla senkronize tutmamız gerekir.


Ölçü birimi sınıfımızı oluşturmak için kullanmamız gereken birimleri, türetilmiş birimleri ve oranları tanımlamanın en iyi yolu nedir? Bir veya daha fazla numaralandırma kullandığını görebiliyordum, ancak bu diğer geliştiriciler için sinir bozucu olabilir. Tek bir numaralandırma 200'den fazla girişle çok büyük olurken, birden fazla numara SI ve Imperial birimlerine göre kafa karıştırıcı olabilir ve birimin kendisinin kategorize edilmesine dayalı olarak ek bir arıza olabilir.

Endişelerimin bazılarını gösteren numaralandırma örnekleri:

myUnits.Volt
myUnits.Newton
myUnits.meter

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

Kullanımdaki birimlerimiz oldukça iyi tanımlanmış ve sonlu bir alan. Müşteri talebi olduğunda yeni türetilmiş birimleri veya oranları genişletme ve ekleme yeteneğine ihtiyacımız var. Daha geniş tasarım yönlerinin birden çok dile uygulanabilir olduğunu düşünmeme rağmen proje C # 'da.


Baktığım kütüphanelerden biri, birimlerin dize yoluyla serbest biçimli girilmesine izin veriyor. Daha sonra UOM sınıfları dizeyi ayrıştırdı ve buna göre oluk açıyor. Bu yaklaşımla ilgili zorluk, geliştiriciyi doğru dize biçimlerinin ne olduğunu düşünmeye ve hatırlamaya zorlamasıdır. Ve yapıcıya geçirilen dizeleri doğrulamak için kod içinde ek denetimler eklemezsek bir çalışma zamanı hatası / istisna riski çalıştırıyorum.

Başka bir kütüphane aslında geliştiricinin birlikte çalışması gereken çok fazla sınıf oluşturdu. Eşdeğer UOM ile birlikte bir sağlanan DerivedUnitve RateUnitvb. Esasen, çözdüğümüz sorunlar için kod aşırı derecede karmaşıktı. Bu kütüphane esasen herhangi bir kombinasyona izin verecektir: (birimler dünyasında meşru olan) herhangi bir kombinasyon (ancak olası her kombinasyona izin vermeyerek) sorunumuzu kapsamaktan (kodumuzu basitleştirmekten) memnuniyet duyuyoruz.

Diğer kütüphaneler gülünç derecede basitti ve örneğin operatör aşırı yüklemesi bile düşünmemişti.

Ayrıca, yanlış dönüşüm girişimleri konusunda endişelenmiyorum (örneğin: volt - metre). Bu noktada bu düzeyde erişebilecek olan yalnızca geliştiricilerdir ve bu tür hatalara karşı korunmamız gerekmez.


Bulduğunuz kütüphanelerin ihtiyaçlarınızı karşılamadığını açıklayabilir misiniz?
13'te svick


1
@MainMa - bu bağlantı için teşekkürler. Sorun alanımız izin verilen dönüşümleri beyan edeceğimiz kadar küçük olduğundan boyutsal analiz yapmamız gerekmez. Üretmek bir zorluklar olacak ama bu bir defalık bir maliyet.

1
Ne tür dönüşümlere ihtiyacınız olduğunu açıklayabilir misiniz? Sadece ölçeklendirme dönüşümü (örneğin metreden santimetreye) mi yoksa boyutlar arası dönüşümler mi (örn. Kütle-kuvvet)?
Bart van Ingen Schenau

1
Kodun bir kısmını F # 'a taşımayı düşündünüz mü? Bu dilin ölçü birimleri vardır.
Pete

Yanıtlar:


11

C ++ için Boost kütüphaneleri , ölçü birimlerini ele alan örnek bir uygulama sunan boyutsal analiz hakkında bir makale içerir .

Özetlemek gerekirse: Ölçü birimleri vektörler olarak temsil edilir, vektörün her bir elemanı temel bir boyutu temsil eder:

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};

Türetilmiş birimler bunların kombinasyonlarıdır. Örneğin, kuvvet (kütle * mesafe / zaman ^ 2) şu şekilde temsil edilir:

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

İmparatorluk ve SI birimleri bir dönüşüm faktörü eklenerek işlenebilir.

Bu uygulama, C ++ 'a özgü tekniklere dayanır (farklı ölçü birimlerini farklı derleme zamanı türlerine kolayca dönüştürmek için şablon meta programlaması kullanarak), ancak kavramlar diğer programlama dillerine aktarılmalıdır.


Yani türetilmiş birimlerin hepsi C ++ sabitlerine eşit mi? Bir şeyleri kirletmekten kaçınmak için bir ad alanına sarıldıklarını sanıyorum?

1
@ GlenH7 - Bu şablon metaprogramlama şeyler içine alır. Aslında mpl::vector_c<int,1,0,0,0,0,0,0>, sabitler yerine ayrı türler (örn. ) Olarak temsil edilirler; Makale ilk olarak açıklama yoluyla consts yaklaşımını sunar (ve muhtemelen bunu iyi açıklamamıştım). Consts kullanmak alternatif olarak işe yarar (derleme zamanı tür güvenliğini kaybedersiniz). Ad kirliliğini önlemek için bir ad alanı kullanmak kesinlikle bir seçenektir.
Josh Kelley

8

Ben sadece Units.NET yayınlandı Github ve üzerinde Nuget .

Size tüm ortak birimleri ve dönüşümleri verir. Hafiftir, ünite test edilmiştir ve PCL'yi destekler.

Sorunuza doğru:

  • Bu, uygulamaların daha hafif ucundadır. Odak, ölçüm birimlerinin açık bir şekilde temsil edilmesine, dönüştürülmesine ve oluşturulmasına yardımcı olmaktır.
  • Denklem çözücü yoktur, hesaplamalardan otomatik olarak yeni birimler türetmez.
  • Birimleri tanımlamak için büyük bir numara.
  • Birimler arasında dinamik olarak dönüştürme için UnitConverter sınıfı.
  • Birimler arasında açıkça dönüştürmek için değişmez veri yapıları.
  • Basit aritmetik için aşırı yüklenmiş operatörler.
  • Yeni birimlere ve dönüşümlere genişletmek, dinamik dönüşüm için yeni bir numaralandırma eklemek ve açık dönüşüm özelliklerini ve operatör aşırı yüklemesini tanımlamak için Uzunluk gibi bir ölçüm sınıfı birimi eklemekle ilgilidir.

Bu alanda henüz çözümlerin kutsal kasesini görmedim. Belirttiğiniz gibi, çalışmak için çok karmaşık veya çok ayrıntılı olabilir. Bazen işleri basit tutmak en iyisidir ve ihtiyaçlarım için bu yaklaşım yeterli olmuştur.

Açık dönüşüm

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

Dinamik dönüşüm

// 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)
Örneğiniz

Ne demek istediğinizden emin değilim, her birim için sadece tek bir değer saklanır. Uzunluk için çift kişilik bir metre alanı tutar ve Kütle için kilogramdır. Diğer birimlere dönüştürürken, bu değeri bir dönüştürme işlevi aracılığıyla çalıştırır. Bu örnekler şimdi biraz modası geçmiş, ancak aynı konsept geçerlidir.
angularsen

Sanırım yanlış yazdım ve sonuçlara atladım ... Demek istedim Tuple. UnitConverterSınıfınızı göremiyorum , ancak IMO, Tuplesınıfla benzer bir işlevi paylaşabileceği gibi görünüyor .
Chef_Code

Tuple karşılaştırmasından hala emin değilim, ancak kullanımla ilgili güncellenmiş örnekler için github sayfasına bakın .
angularsen

3

C # yerine F # anahtarını çekebiliyorsanız, F #, yapmaya çalıştığınız şeye sığacak gibi görünen bir ölçü birimine (değerler üzerinde meta veriler kullanılarak uygulanır) sahiptir:

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

Özellikle:

// 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 *)

İyi bir öneri ve biz bunu düşündük. F # 'ın bize birimlerin çıktıda nasıl görüntüleneceğini kontrol etme yeteneği verdiğine inanmıyorum.

2
@ GlenH7 Haklı olduğuna inanıyorum: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

Gerekli tüm dönüşümlerin dönüşümleri ölçeklendirmesi gerçeğine dayanarak (sıcaklık dönüşümlerini desteklemeniz dışında. Dönüşümün bir dengelemeyi içerdiği hesaplamalar önemli ölçüde daha karmaşıktır), 'ölçüm birimi' sistemimi şöyle tasarlarım:

  • Bir unitölçeklendirme faktörü, birimin metinsel gösterimi için bir dize ve ölçeklenen bir referans içeren bir sınıf unit. Metinsel gösterim, görüntüleme amacıyla ve farklı birimlerle değerler üzerinde matematik yaparken sonucun hangi birimde olduğunu bilmek için ana birime referans olarak bulunur.

    Desteklenen her birim için, unitsınıfın statik bir örneği sağlanır.

  • Bir UOMdeğer ve değere başvuru içeren bir sınıf unit. UOMSınıf / ekleme bir çıkarma için operatörleri aşırı yüklenmiş UOMve çarpma / boyutsuz bir değeri ile bölmek için.

    Toplama / çıkarma UOMaynı ile ikisinde yapılırsa unit, doğrudan yapılır. Aksi takdirde her iki değer de ilgili temel birimlere dönüştürülür ve toplanır / çıkarılır. Sonuç tabanda olduğu rapor edilmiştir unit.

Kullanım gibi olurdu

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);

Uyumsuz birimlerdeki işlemler bir sorun olarak değerlendirilmediğinden, tasarımı bu açıdan güvenli hale getirmeye çalışmadım. İki birimin aynı ana birime atıfta bulunduğunu doğrulayarak bir çalışma zamanı kontrolü eklemek mümkündür.


Sıcaklıktan bahsettiğinizden beri: nedir 95F - 85F? Nedir 20C - 15C? Her iki örnekte de, her ikisi UOMde aynı olacaktır unit. Çıkarmalar doğrudan yapılacak mı?

@MattFenwick: Sonuçlar sırasıyla 10 Fve olur 5 C. Gereksiz dönüşümleri önlemek için hesaplamalar mümkünse doğrudan yapılır. Birim dönüştürme yöntemlerini eklemek oldukça önemsiz olacaktır UOM, ancak Celsius-Fahrenheit dönüşümü için unitsınıfın bir ölçeklendirme faktörüne ek olarak bir ofset olasılığı ile genişletilmesi gerekecektir.
Bart van Ingen Schenau

Ama 95F - 85F! = 10F.

1
@MattFenwick: Lütfen beni aydınlatın. Eğer sıcaklık düşürmek eğer nasıl soğuk olsun 95Ftarafından 85F? Bildiğim kadarıyla Fahrenheit hala doğrusal bir ölçek.
Bart van Ingen Schenau

2
Celsius örneğini yapalım çünkü Kelvin'e dönüştürmek daha kolay: eğer dersek 20C - 15C = 5C, diyoruz ki 293.15K - 288.15K = 278.15K, bu açıkça yanlış.

2

Kodunuzun ne yaptığını ve neye izin vereceğini düşünün. İçindeki tüm olası birimlerle basit bir numaralandırmaya sahip olmak, Volt'u metreye dönüştürmek gibi bir şey yapmama izin veriyor. Bu bir insan için geçerli değil, ancak yazılım memnuniyetle deneyecek.

Bir keresinde buna benzer bir şey yaptım ve uygulamamın hepsinin uyguladığı soyut temel sınıfları (uzunluk, ağırlık vb.) Vardı IUnitOfMeasure. Her abstract bases sınıfı , tüm dönüştürme çalışmaları için kullanacağı varsayılan bir tür (sınıfın Lengthvarsayılan bir sınıf uygulaması vardı) tanımladı Meter. Böylece IUnitOfMeasureiki farklı yöntem uygulanmıştır ToDefault(decimal)ve FromDefault(decimal).

Sarmak istediğim gerçek sayı IUnitOfMeasure, genel argümanı olarak kabul edilen genel bir türdü. Böyle bir şey söylemek Measurement<Meter>(2.0)size otomatik tip güvenlik sağlar. Bu sınıflara uygun örtülü dönüşümleri ve matematik yöntemlerini uygulamak Measurement<Meter>(2.0) * Measurement<Inch>(12), varsayılan tür ( Meter) gibi bir şey yapmanıza ve sonuç döndürmenize olanak tanır . Newton gibi türetilmiş birimler hiç çalışmadım; Onları sadece Kilogram * Metre / Saniye / Saniye olarak bıraktım.


Genel tiplerin kullanımı ile önerdiğiniz yaklaşımı seviyorum.

1

Yanıtın MarioVW'nin Stack Overflow yanıtında yattığına inanıyorum :

Pratik Örnek Tuple .Net 4-0'da nerede kullanılabilir?

Tuples ile kolayca iki boyutlu bir sözlük (veya bu konu için n-boyutlu) uygulayabilirsiniz. Örneğin, bir döviz değişimi eşlemesi uygulamak için böyle bir sözlük kullanabilirsiniz:

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

Başvurum için de benzer bir ihtiyacım vardı. Tupleaynı zamanda değişmezdir ki bu da Ağırlıklar ve Ölçüler gibi nesneler için de geçerlidir.


0

Prototip kodum: http://ideone.com/x7hz7i

Tasarım noktalarım:

  1. Mülk edinme / ayarlama olarak UoM (Ölçü Birimi) seçimi
    Uzunluk len = yeni Uzunluk ();
    len.Meters = 2.0;
    Console.WriteLine (len.Feet);
    
  2. UoM seçimi için adlandırılmış kurucu
    Uzunluk len = Uzunluk Metreler (2.0);
    
  3. UoM için ToString desteği
    Console.WriteLine (len.ToString ( "ft"));
    Console.WriteLine (len.ToString ( "F15"));
    Console.WriteLine (len.ToString ( "ftF15"));
    
  4. Gidiş dönüş dönüşümü (çifte kesinlik nedeniyle ihmal edilebilir yuvarlama kaybı)
    Uzunluk lenRT = Uzunluk.FromMeters (Uzunluk.
    
  5. Operatör aşırı yüklenmesi (ancak boyut tipi kontrolünden yoksun )
    // Oldukça dağınık, buggy, güvensiz ve F # veya C ++ MPL kullanılmadan mümkün olmayabilir.
    // O söylemeye devam ediyor Boyutlu Analiz olan isteğe bağlı bir özellik değil UoM için -
    // doğrudan kullanıp kullanmadığınız. O edilir gerekli .
    

0

Magazinde iyi bir makale var, Almanca: http://www.dotnetpro.de/articles/onlinearticle1398.aspx

Temel fikir, BaseMeasurement ile Uzunluk gibi bir Unit sınıfına sahip olmaktır. Sınıf, dönüşüm faktörünü, operatör aşırı yüklerini, ToString aşırı yüklerini, dizelerin ayrıştırıcısını ve dizin oluşturucu olarak bir uygulamayı içerir. Mimari görüşü bile uygulamaya koyduk, ancak kütüphane olarak yayınlanmadı.

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);
        }

...

Basınç operatörü ile veya sadece kullanımı görürsünüz:

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];

Ama dediğin gibi, ben de tek boynuzlu atı bulamadım :)


-1

Bu, ilişkilerini belirtmek için veri dosyası odaklı bir yaklaşım kullanarak Unix komutunun varoluş sebebidirunits .


Bahsettiğiniz için teşekkürler units. Birimlerin daha geniş çözümüm için çalışmamasının ana nedeni serbest form dizeleridir. Kabul edilirse, hata mesajları verir, ancak bu yaklaşım bu kodu uygulamamızla entegre edecek geliştiricileri hedeflemektedir. Serbest biçimli dizeler hata için çok fazla fırsat sunar.

1
units'In veri dosyasına bir göz atmalısınız . Miktarlar arasındaki ilişkileri tanımlama şekli çok temiz ve probleminiz için yararlı olabilir.
Ross Patterson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.