Struktur data untuk mengakses satuan ukuran

17

TL; DR - Saya mencoba merancang struktur data yang optimal untuk mendefinisikan unit dalam satuan ukuran.


A Unit of measurepada dasarnya adalah value(atau kuantitas) yang terkait dengan a unit. Unit SI memiliki tujuh pangkalan atau dimensi. Yaitu: panjang, massa, waktu, arus listrik, suhu, jumlah zat (mol), dan intensitas cahaya.

Ini akan cukup mudah, tetapi ada sejumlah unit turunan serta tarif yang sering kita gunakan. Satuan gabungan contoh adalah Newton: kg * m / s^2dan contoh laju adalah tons / hr.

Kami memiliki aplikasi yang sangat bergantung pada unit tersirat. Kami akan menyematkan unit dalam nama variabel atau kolom. Tapi ini menciptakan masalah ketika kita perlu menentukan satuan ukuran dengan unit yang berbeda. Ya, kami dapat mengonversi nilai pada input dan tampilan tetapi ini menghasilkan banyak kode overhead yang ingin kami enkapsulasi di dalam kelasnya sendiri.

Ada sejumlah solusi pada codeplex dan lingkungan kolaboratif lainnya. Lisensi untuk proyek-proyek tersebut disetujui tetapi proyek itu sendiri biasanya berakhir terlalu ringan atau terlalu berat. Kami mengejar unicorn kami sendiri "benar."

Idealnya, saya bisa mendefinisikan satuan ukuran baru menggunakan sesuatu seperti ini:

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

Tentu saja, kami menggunakan campuran unit Imperial dan SI berdasarkan kebutuhan klien kami.

Kami juga perlu menjaga struktur unit ini disinkronkan dengan tabel basis data masa depan sehingga kami dapat memberikan tingkat konsistensi yang sama di dalam data kami juga.


Apa cara terbaik untuk mendefinisikan unit, unit turunan, dan kurs yang perlu kita gunakan untuk membuat unit kelas pengukuran kita? Saya bisa melihat menggunakan satu atau lebih enum, tetapi itu bisa membuat frustasi bagi pengembang lain. Satu enum akan sangat besar dengan 200+ entri sedangkan beberapa enum bisa membingungkan berdasarkan unit SI vs Imperial dan rincian tambahan berdasarkan kategorisasi unit itu sendiri.

Contoh-contoh Enum yang menunjukkan beberapa kekhawatiran saya:

myUnits.Volt
myUnits.Newton
myUnits.meter

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

Seperangkat unit yang kami gunakan didefinisikan dengan cukup baik dan ini adalah ruang yang terbatas. Kami benar-benar membutuhkan kemampuan untuk memperluas dan menambahkan unit atau tarif turunan baru ketika kami memiliki permintaan klien untuk mereka. Proyek ini dalam C # meskipun saya pikir aspek desain yang lebih luas berlaku untuk berbagai bahasa.


Salah satu perpustakaan yang saya lihat memungkinkan untuk input bebas bentuk unit melalui string. Kelas UOM mereka kemudian mengurai string dan mengatur hal-hal yang sesuai. Tantangan dengan pendekatan ini adalah bahwa hal itu memaksa pengembang untuk berpikir dan mengingat apa format string yang benar. Dan saya menjalankan risiko kesalahan runtime / pengecualian jika kita tidak menambahkan pemeriksaan tambahan dalam kode untuk memvalidasi string yang diteruskan dalam konstruktor.

Pustaka lain pada dasarnya menciptakan terlalu banyak kelas yang harus dikerjakan pengembang. Seiring dengan UOM yang setara itu disediakan DerivedUnitdan RateUnitdan seterusnya. Pada dasarnya, kode itu terlalu rumit untuk masalah yang kami pecahkan. Pustaka itu pada dasarnya akan membolehkan apa saja: kombinasi apa pun (yang sah di dunia unit) tapi kami senang lingkup masalah kami (menyederhanakan kode kami) dengan tidak mengizinkan setiap kombinasi yang mungkin.

Perpustakaan lain sangat sederhana dan bahkan tidak mempertimbangkan kelebihan operator misalnya.

Selain itu, saya tidak khawatir tentang upaya konversi yang salah (misalnya: volt ke meter). Devs adalah satu-satunya yang akan mengakses pada tingkat ini pada saat ini dan kita tidak perlu melindungi dari jenis kesalahan tersebut.


sumber
Bisakah Anda menjelaskan dengan cara apa perpustakaan yang Anda temukan tidak sesuai dengan kebutuhan Anda?
svick
1
Lihat juga stackoverflow.com/q/348853/240613
Arseni Mourzenko
1
@MainMa - terima kasih untuk tautan itu. Kami tidak perlu melakukan analisis dimensi karena ruang masalah kami cukup kecil sehingga kami hanya akan mendeklarasikan konversi yang diizinkan. Ini akan menjadi kerja keras untuk menghasilkan tetapi itu adalah biaya satu kali.
1
Bisakah Anda menjelaskan konversi seperti apa yang Anda butuhkan? Apakah hanya konversi konversi (mis. Meter ke sentimeter) atau juga konversi lintas dimensi (mis. Massa untuk memaksa)?
Bart van Ingen Schenau
1
Sudahkah Anda mempertimbangkan untuk memindahkan sebagian kode ke F #? Bahasa itu memiliki satuan ukuran build int.
Pete

Jawaban:

11

Perpustakaan Boost untuk C ++ termasuk artikel tentang analisis dimensi yang menyajikan contoh implementasi unit penanganan ukuran.

Untuk meringkas: Satuan pengukuran direpresentasikan sebagai vektor, dengan masing-masing elemen vektor mewakili dimensi mendasar:

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

Unit yang diturunkan adalah kombinasi dari keduanya. Sebagai contoh, gaya (massa * jarak / waktu ^ 2) akan direpresentasikan sebagai

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

Unit Imperial versus SI dapat ditangani dengan menambahkan faktor konversi.

Implementasi ini bergantung pada C ++ - teknik khusus (menggunakan metaprogramming template untuk dengan mudah mengubah unit pengukuran yang berbeda menjadi tipe waktu kompilasi yang berbeda), tetapi konsep-konsep tersebut harus ditransfer ke bahasa pemrograman lain.

Josh Kelley
sumber
Jadi semua unit turunan setara dengan konstanta C ++? Saya kira mereka terbungkus dalam namespace untuk menghindari hal-hal yang mencemari?
1
@ GlenH7 - Ini masuk ke hal-hal metaprogramming template. Mereka sebenarnya direpresentasikan sebagai tipe yang terpisah (misalnya, mpl::vector_c<int,1,0,0,0,0,0,0>) bukan const; artikel ini menyajikan pendekatan consts pertama melalui penjelasan (dan saya mungkin tidak menjelaskannya dengan baik). Menggunakan const akan bekerja sebagai alternatif (Anda akan kehilangan beberapa jenis keamanan waktu kompilasi). Menggunakan namespace untuk menghindari polusi nama tentu saja merupakan pilihan.
Josh Kelley
8

Saya baru saja merilis Units.NET di Github dan di NuGet .

Ini memberi Anda semua unit umum dan konversi. Ini ringan, unit diuji dan mendukung PCL.

Menuju pertanyaan Anda:

  • Ini adalah ujung implementasi yang lebih ringan. Fokusnya adalah untuk membantu representasi, konversi, dan konstruksi unit pengukuran yang tidak ambigu.
  • Tidak ada pemecah persamaan, itu tidak secara otomatis menurunkan unit baru dari perhitungan.
  • Satu enum besar untuk mendefinisikan unit.
  • Kelas UnitConverter untuk mengkonversi secara dinamis antara unit.
  • Struktur data yang tidak dapat diubah untuk mengkonversi secara eksplisit antar unit.
  • Operator kelebihan beban untuk aritmatika sederhana.
  • Memperluas ke unit dan konversi baru adalah masalah menambahkan enum baru untuk konversi dinamis dan menambahkan unit kelas pengukuran, seperti Panjang, untuk menentukan properti konversi eksplisit dan kelebihan operator.

Saya belum melihat cawan suci solusi di domain ini. Ketika Anda menyatakan, itu bisa dengan mudah menjadi terlalu rumit atau terlalu bertele-tele untuk dikerjakan. Terkadang, yang terbaik adalah menjaga segala sesuatunya sederhana dan untuk kebutuhan saya pendekatan ini telah terbukti memadai.

Konversi eksplisit

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

Konversi dinamis

// 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);
angularsen
sumber
Contoh Anda pada dasarnya tampaknya memanfaatkan aTruple<T1, T2, T3>(x, y, z)
Chef_Code
Tidak yakin apa yang Anda maksud, hanya ada satu nilai yang disimpan untuk setiap unit. Untuk Panjang itu memegang bidang meter jenis ganda, dan untuk Massa itu adalah kilogram. Ketika mengkonversi ke unit lain itu menjalankan nilai itu melalui fungsi konversi. Sampel-sampel ini agak ketinggalan jaman sekarang, tetapi konsep yang sama berlaku.
angularsen
Saya kira saya salah bicara dan langsung mengambil kesimpulan ... Maksud saya Tuple. Saya tidak dapat melihat UnitConverterkelas Anda , tetapi IMO sepertinya memiliki fungsi yang sama dengan Tuplekelas.
Chef_Code
Masih tidak yakin tentang perbandingan Tuple, tetapi lihat halaman github untuk contoh terbaru tentang penggunaan.
angularsen
3

Jika Anda dapat menarik beralih ke F # sebagai gantinya menggunakan C #, F # memiliki unit sistem pengukuran (diimplementasikan menggunakan metadata pada nilai-nilai) yang sepertinya cocok dengan apa yang Anda coba lakukan:

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

Terutama:

// 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 *)
paul
sumber
Saran yang bagus, dan kami sudah mempertimbangkannya. Saya tidak percaya F # memberi kita kemampuan untuk mengontrol bagaimana unit akhirnya ditampilkan pada output.
2
@ GlenH7 Saya yakin Anda benar: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

Berdasarkan kenyataan bahwa semua konversi yang diperlukan adalah konversi skala (kecuali jika Anda harus mendukung konversi suhu. Perhitungan di mana konversi melibatkan offset secara signifikan lebih kompleks), saya akan merancang sistem 'unit pengukuran' saya seperti ini:

  • Kelas yang unitmengandung faktor penskalaan, string untuk representasi tekstual unit dan referensi yang menjadi unitskala. Representasi tekstual ada untuk tujuan tampilan dan referensi ke unit dasar untuk mengetahui unit mana hasilnya ketika melakukan matematika pada nilai-nilai dengan unit yang berbeda.

    Untuk setiap unit yang didukung, instance statis unitkelas disediakan.

  • Kelas yang UOMberisi nilai dan referensi ke nilai unit. The UOMkelas menyediakan kelebihan beban operator untuk menambahkan / mengurangkan lain UOMdan untuk mengalikan / membagi dengan nilai berdimensi.

    Jika penambahan / pengurangan dilakukan pada dua UOMdengan yang sama unit, itu dilakukan secara langsung. Kalau tidak, kedua nilai dikonversi ke unit dasar masing-masing dan ditambahkan / dikurangi. Hasilnya dilaporkan berada di pangkalan unit.

Penggunaannya akan seperti

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

Karena operasi pada unit yang tidak kompatibel tidak dianggap sebagai masalah, saya belum mencoba untuk membuat tipe desain aman dalam hal itu. Dimungkinkan untuk menambahkan pemeriksaan runtime dengan memverifikasi bahwa dua unit merujuk ke unit dasar yang sama.

Bart van Ingen Schenau
sumber
Karena Anda menyebutkan suhu: apa itu 95F - 85F? Apa 20C - 15C? Dalam kedua contoh, keduanya UOMakan memiliki hal yang sama unit. Apakah pengurangan akan dilakukan secara langsung?
@MattFenwick: Hasilnya masing-masing 10 Fdan 5 C. Perhitungan dilakukan secara langsung jika memungkinkan, untuk menghindari konversi yang tidak dibutuhkan. Akan sangat sepele untuk menambahkan metode konversi satuan UOM, tetapi untuk konversi Celsius-Fahrenheit, unitkelas harus diperluas dengan kemungkinan offset selain faktor penskalaan.
Bart van Ingen Schenau
Tapi 95F - 85F! = 10F.
1
@MattFenwick: Tolong beri tahu saya. Seberapa dingin itu jika Anda menurunkan suhu 95Foleh 85F? Setahu saya, Fahrenheit masih skala linear.
Bart van Ingen Schenau
2
Mari kita lakukan contoh Celsius karena lebih mudah dikonversi ke Kelvin: jika kita katakan 20C - 15C = 5C, maka kita katakan 293.15K - 288.15K = 278.15K, yang jelas salah.
2

Pikirkan tentang apa yang dilakukan kode Anda, dan apa yang akan diizinkan. Memiliki penghitungan sederhana dengan semua unit yang memungkinkan di dalamnya memungkinkan saya untuk melakukan sesuatu seperti mengkonversi Volts ke meter. Itu jelas tidak berlaku untuk manusia, tetapi perangkat lunak akan dengan senang hati mencoba.

Saya melakukan sesuatu yang agak mirip dengan ini sekali, dan implementasi saya memiliki kelas dasar abstrak (panjang, berat, dll.) Yang semuanya diimplementasikan IUnitOfMeasure. Setiap kelas basis abstrak mendefinisikan jenis default (kelas Lengthmemiliki implementasi standar kelas Meter) yang akan digunakan untuk semua pekerjaan konversi. Karenanya IUnitOfMeasurediimplementasikan dua metode yang berbeda, ToDefault(decimal)dan FromDefault(decimal).

Angka aktual yang ingin saya bungkus adalah tipe generik yang menerima IUnitOfMeasuresebagai argumen generiknya. Mengatakan sesuatu seperti Measurement<Meter>(2.0)memberi Anda keamanan tipe otomatis. Menerapkan konversi implisit yang tepat dan metode matematika pada kelas-kelas ini memungkinkan Anda untuk melakukan hal-hal seperti Measurement<Meter>(2.0) * Measurement<Inch>(12)dan mengembalikan hasil dalam tipe default ( Meter). Saya tidak pernah mengerjakan unit turunan seperti Newtons; Saya hanya meninggalkan mereka sebagai Kilogram * Meter / Detik / Detik.

mgw854
sumber
Saya suka pendekatan yang Anda sarankan dengan menggunakan tipe generik.
1

Saya percaya jawabannya terletak pada respons Stack Overflow MarioVW terhadap :

Contoh Praktis Di mana Tuple dapat digunakan dalam .Net 4-0?

Dengan tuple Anda dapat dengan mudah mengimplementasikan kamus dua dimensi (atau n-dimensional dalam hal ini). Misalnya, Anda dapat menggunakan kamus seperti itu untuk menerapkan pemetaan pertukaran mata uang:

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

Saya memiliki kebutuhan yang serupa untuk aplikasi saya. Tuplejuga tidak berubah yang juga berlaku untuk benda-benda seperti Bobot dan Ukuran ... Seperti kata pepatah "satu liter per pon dunia di sekitar."

Chef_Code
sumber
0

Kode prototipe saya: http://ideone.com/x7hz7i

Poin desain saya:

  1. Pilihan UoM (Unit Ukur) sebagai Properti dapatkan / set
    Panjang len = Panjang baru ();
    len.Meters = 2.0;
    Console.WriteLine (len.Feet);
    
  2. Dinamakan konstruktor untuk pilihan UoM
    Length len = Length.FromMeters (2.0);
    
  3. Dukungan ToString untuk UoM
    Console.WriteLine (len.ToString ("ft"));
    Console.WriteLine (len.ToString ("F15"));
    Console.WriteLine (len.ToString ("ftF15"));
    
  4. Konversi pulang pergi (kehilangan pembulatan yang dapat diabaikan diizinkan oleh presisi ganda)
    Panjang lenRT = Panjang.FromMeters (Panjang.FromFeet (Panjang.FromMeters (len.Meters) .Feet) .Meter);
    
  5. Overloading operator (tetapi tidak ada pemeriksaan tipe dimensional)
    // Cukup berantakan, buggy, tidak aman, dan mungkin tidak dapat dilakukan tanpa menggunakan F # atau C ++ MPL.
    // Ini melanjutkan dengan mengatakan bahwa Analisis Dimensi adalah bukan fitur opsional untuk UoM -
    // apakah Anda menggunakannya secara langsung atau tidak. Itu wajib .
    
rwong
sumber
0

Ada sebuah artikel yang baik dalam magazin sebuah unfourtunatly itu adalah dalam bahasa Jerman: http://www.dotnetpro.de/articles/onlinearticle1398.aspx

Ide dasar adalah memiliki kelas Unit seperti Panjang dengan BaseMeasurement. Kelas berisi faktor konversi, kelebihan operator, kelebihan ToString, pengurai string dan implementasi sebagai pengindeks. Kami bahkan telah mengimplementasikan tampilan Arsitektur tetapi tidak dirilis sebagai perpustakaan.

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

...

Jadi Anda melihat penggunaan dengan operator Tekanan atau hanya:

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

Tetapi seperti yang Anda katakan, saya juga tidak menemukan unicorn :)

KCT
sumber
-1

Ini adalah raison d'etre dari unitsperintah Unix , yang melakukan semuanya menggunakan pendekatan data-file untuk menentukan hubungan.

Ross Patterson
sumber
Terima kasih telah menyebutkan units. Alasan utama unit tidak bekerja untuk solusi saya yang lebih luas adalah string bentuk bebas. Memang, itu memberikan pesan kesalahan sebagai imbalan, tetapi pendekatan ini menargetkan pengembang yang akan mengintegrasikan kode ini dengan aplikasi kami. String bentuk bebas menyajikan terlalu banyak peluang untuk kesalahan.
1
Anda harus melihat pada unitsfile data. Cara mendefinisikan hubungan antara jumlah sangat bersih, dan mungkin berguna untuk masalah Anda.
Ross Patterson