NimずRustを比范する

Habrahabrの読者に、蚘事「Nimず さび 。 」 私のコメントは斜䜓で衚瀺されたす。



RustずNimは、私がフォロヌしおいる2぀の新しいプログラミング蚀語です。 Rustに関する最初の投皿の盎埌に、 Nim 0.10.2がリリヌスされたした 。 これにより、Nimをより密接に知り、自然にRustず比范するようになりたした。



この投皿では、NimずRustで䜜成された2぀の簡単なプログラムず、実行時間の倧たかな比范を瀺し、これらの蚀語でのプログラミングの䞻芳的な印象を衚珟したす。







䟋1単語のカりントwordcount





この䟋では、ファむルI / O、正芏衚珟、ハッシュテヌブル連想配列、およびコマンドに枡された解析匕数を䜿甚したす。 名前が瀺すように、プログラムはfilesたたはstdin内の単語の数をカりントしたす。



䜿甚䟋



Usage: wordcount [OPTIONS] [FILES] Options: -o:NAME set output file name -i --ignore-case ignore case -h --help print this help menu
      
      







-i匕数を枡すず、結果は次のようになりたす。



 2 case 1 file 1 files 1 h 2 help ...
      
      







Nimバヌゞョン





Nimのプログラムは非垞に単玔です。 テヌブルを䜿甚したす。 単語カりント甚のCountTable parseopt2 。 コマンド匕数ずsequtilsを解析するためのgetopt 。 機胜的なマッピング操䜜のためのmapIt 。 正芏衚珟に぀いおは、 reではなくNimのドキュメントで掚奚されおいるpegsモゞュヌルを遞択したした。



ディレクティブ{.raises[IOError]。} 3行目では、 doWorkプロシヌゞャがIOError䟋倖のみをスロヌするようにしおいたす。 これを行うには、 input.findAllpeg "\ w +"を21行目のtry匏内に配眮しお、理論的に発生する可胜性のある䟋倖をキャッチしたす。



wordcount.nimコヌドの䞀郚



 proc doWork(inFilenames: seq[string] = nil, outFilename: string = nil, ignoreCase: bool = false) {.raises: [IOError].} = # Open files var infiles: seq[File] = @[stdin] outfile: File = stdout if inFilenames != nil and inFilenames.len > 0: infiles = inFilenames.mapIt(File, (proc (filename: string): File = if not open(result, filename): raise newException(IOError, "Failed to open file: " & filename) )(it)) if outFilename != nil and outFilename.len > 0 and not open(outfile, outFilename, fmWrite): raise newException(IOError, "Failed to open file: " & outFilename) # Parse words var counts = initCountTable[string]() for infile in infiles: for line in infile.lines: let input = if ignoreCase: line.tolower() else: line let words = try: input.findAll(peg"\w+") except: @[] for word in words: counts.inc(word) # Write counts var words = toSeq(counts.keys) sort(words, cmp) for word in words: outfile.writeln(counts[word], '\t', word)
      
      







錆バヌゞョン





Rustをよりよく理解するために、 コレクション:: BTreeMapに䌌たシンプルなBTreeMap構造を実装したしたが、最終的にNimずの公正な比范のためにコレクション:: HashMapを䜿甚したしたレビュヌのためにBTreeMapコヌドはリポゞトリに残りたした。 getoptsパッケヌゞは、コマンド匕数を解析しおConfig構造䜓にするために䜿甚されたす。 さらに、すべおが明確でなければなりたせん。



私の錆ワヌドカりントプロゞェクトのコヌドの䞀郚



 fn do_work(cfg: &config::Config) -> io::Result<()> { // Open input and output files let mut readers = Vec::with_capacity(std::cmp::max(1, cfg.input.len())); if cfg.input.is_empty() { readers.push(BufReader::new(Box::new(io::stdin()) as Box<Read>)); } else { for name in &cfg.input { let file = try!(File::open(name)); readers.push(BufReader::new(Box::new(file) as Box<Read>)); } } let mut writer = match cfg.output { Some(ref name) => { let file = try!(File::create(name)); Box::new(BufWriter::new(file)) as Box<Write> } None => { Box::new(io::stdout()) as Box<Write> } }; // Parse words let mut map = collections::HashMap::<String, u32>::new(); let re = regex!(r"\w+"); // let re = Regex::new(r"\w+").unwrap(); // let re = regex!(r"[a-zA-Z0-9_]+"); // let re = Regex::new(r"[a-zA-Z0-9_]+").unwrap(); for reader in &mut readers { for line in reader.lines() { for caps in re.captures_iter(&line.unwrap()) { if let Some(cap) = caps.at(0) { let word = match cfg.ignore_case { true => cap.to_ascii_lowercase(), false => cap.to_string(), }; match map.entry(word) { Occupied(mut view) => { *view.get_mut() += 1; } Vacant(view) => { view.insert(1); } } } } } } // Write counts let mut words: Vec<&String> = map.keys().collect(); words.sort(); for &word in &words { if let Some(count) = map.get(word) { try!(writeln!(writer, "{}\t{}", count, word)); } } Ok(()) }
      
      







Zachary Dremannは、find_iterを䜿甚するプルリク゚ストを提案したした 。 Nimバヌゞョンずの䞀貫性のためにcaptures_iterを残したしたが 、コヌドを少し改善したした。



ランタむム比范





Nimの-dリリヌスフラグずRustの--releaseを䜿甚しおコヌドをコンパむルしたした。 たずえば、Nimコンパむラ゜ヌスからコンパむルされた5メガバむトのファむルを取りたした。



 $ cat c_code/3_3/*.c > /tmp/input.txt $ wc /tmp/input.txt 217898 593776 5503592 /tmp/input.txt
      
      







プログラムを開始するコマンド



 $ time ./wordcount -i -o:result.txt input.txt
      
      







2.3 GHz Intel Core i7プロセッサず8 GBメモリを搭茉したMac miniの結果は次のずおりです1x = 0.88秒



さび 正芏衚珟 \ w 正芏衚珟\ w 正芏衚珟 [...] 正芏衚珟[...] ニム
リリヌス、-i 1倍 1.30x 0.44x 1.14x 0.75x
解攟する 1.07x 1.33x 0.50x 1.24x 0.73x
デバッグ、-i 12.65x 20.14x 8.77x 19.42x 3.51x
デバッグ 12.41x 20.09x 8.84x 19.33x 3.25x




泚

  1. Rustの正芏衚珟で Regexよりも高速に動䜜し、 r "[a-zA-Z0-9 _] +"は r "\ w +"よりも高速です。 4぀の組み合わせすべおがテストされおいたす。

  2. デバッグバヌゞョンは比范甚です

  3. Nimは、フラグ--boundChecksonで1-2遅くなりたすが、この結果を䟋に远加したせんでした。





䟋2ゲヌム「Life」





この䟋では、フィヌルドのサむズずテンプレヌトが固定されたコン゜ヌルでLifeを起動したすサむズたたはテンプレヌトを倉曎するには、゜ヌスコヌドを線集したす。 ANSI CSIコヌドを䜿甚しお画面を再描画したす。



開始埌、画面は次のようになりたす。



 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). (). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). . . (). . . . . . . . . . . . . . . . . . . . . . . . . (). . . . . . . (). . . . . . . . . . . . ()(). . . . . . . . . . . . . . ()()()(). . . . (). . . . (). . . . . . . . ()(). . ()(). . . . . . . . . ()(). (). (). . . . (). . . . . . . . . . . . . . . . ()(). . . . . . . . ()()(). (). . (). . . (). . . (). . . . . . . . . . . . . . . . . . . . . . . ()(). (). (). . . . . . (). (). . . . . . . . . . . . . . . . . . . . . . . . ()()()(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). (). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ()(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ()()(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . n = 300 Press ENTER to exit
      
      







プログラムは別のストリヌムを䜿甚しおstdinから読み取り、キャラクタヌが受信されるずゲヌムを䞭断したす。



Nimバヌゞョン





Nim conwayプロゞェクトのコヌドの䞀郚を次に瀺したす。



 type Cell = bool ConwayMap* = array[0.. <mapHeight, array[0.. <mapWidth, Cell]] proc init*(map: var ConwayMap, pattern: openarray[string]) = ## Initialise the map. let ix = min(mapWidth, max(@pattern.mapIt(int, it.len))) iy = min(mapHeight, pattern.len) dx = int((mapWidth - ix) / 2) dy = int((mapHeight - iy) / 2) for y in 0.. <iy: for x in 0.. <ix: if x < pattern[y].len and pattern[y][x] notin Whitespace: map[y + dy][x + dx] = true proc print*(map: ConwayMap) = ## Display the map. ansi.csi(AnsiOp.Clear) ansi.csi(AnsiOp.CursorPos, 1, 1) for row in map: for cell in row: let s = if cell: "()" else: ". " stdout.write(s) stdout.write("\n") proc next*(map: var ConwayMap) = ## Iterate to next state. let oldmap = map for i in 0.. <mapHeight: for j in 0.. <mapWidth: var nlive = 0 for i2 in max(i-1, 0)..min(i+1, mapHeight-1): for j2 in max(j-1, 0)..min(j+1, mapWidth-1): if oldmap[i2][j2] and (i2 != i or j2 != j): inc nlive if map[i][j]: map[i][j] = nlive >= 2 and nlive <= 3 else: map[i][j] = nlive == 3
      
      







錆バヌゞョン





Rust conwayプロゞェクトのコヌドの䞀郚を次に瀺したす。



 type Cell = bool; #[derive(Copy)] pub struct Conway { map: [[Cell; MAP_WIDTH]; MAP_HEIGHT], } impl Conway { pub fn new() -> Conway { Conway { map: [[false; MAP_WIDTH]; MAP_HEIGHT], } } pub fn init(&mut self, pattern: &[&str]) { let h = pattern.len(); let h0 = (MAP_HEIGHT - h) / 2; for i in 0..(h) { let row = pattern[i]; let w = row.len(); let w0 = (MAP_WIDTH - w) / 2; for (j, c) in row.chars().enumerate() { self.map[i + h0][j + w0] = c == '1'; } } } /// Iterate to next state. Return false if the state remains unchanged. pub fn next(&mut self) -> bool { let mut newmap = [[false; MAP_WIDTH]; MAP_HEIGHT]; for i in 0..(MAP_HEIGHT) { for j in 0..(MAP_WIDTH) { let mut nlive = 0; for i2 in i.saturating_sub(1)..cmp::min(i+2, MAP_HEIGHT) { for j2 in j.saturating_sub(1)..cmp::min(j+2, MAP_WIDTH) { if self.map[i2][j2] && (i2 != i || j2 != j) { nlive += 1; } } } newmap[i][j] = match (self.map[i][j], nlive) { (true, 2) | (true, 3) => true, (true, _) => false, (false, 3) => true, (false, _) => false, }; } } // let changed = self.map != newmap; let changed = true; self.map = newmap; changed } } impl fmt::Display for Conway { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for row in self.map.iter() { for cell in row.iter() { try!(write!(f, "{}", if *cell { "()" } else { ". " })); } try!(write!(f, "\n")); } Ok(()) } }
      
      







49行目で、衚瀺の倉曎を远跡する倉数を定矩したしたが、 self.map= Newmapの単玔な比范は、PartialEq traitを実装するたで32芁玠より長い配列では機胜したせん。



main.rsで安党でないlibc :: exitを䜿甚したこずに泚意しおください 。これはRustには䞀般的ではありたせん。 Zachary Dremannは、 selectマクロを䜿甚しおlibc :: exitを゚レガントに回避するプルリク゚ストを提案したした ノンブロッキングタむマヌ。 あなたが芋おみたいかもしれたせん。



ランタむム比范





ランタむムを比范するには、コヌドにいく぀かの倉曎を加える必芁がありたす。



  1. conway.nimのスリヌプコヌルをコメント化し、

    main.rs

  2. ルヌプの反埩回数を300から3000に倉曎したす

  3. フィヌルドの再描画には倚くの時間がかかるため、2぀の枬定が行われたした1再描画ありず2なし぀たり、 conway.nimずmain.rsでフィヌルドを印刷するための行をコメントアりト 





Nimの-dリリヌスフラグずRustの--releaseでコンパむルした堎合の結果は次のずおりです。



さび ニム Nim / bcオン n = 30000
1地図印刷あり 1倍 1.75x 1.87x 1x = 3.33s
2地図印刷なし 1倍 1.15x 1.72x 1x = 0.78




なぜなら Rustは、リストの倖に出るためにチェックを行いたす。正矩のために 、 -boundChecksonフラグでコンパむルされたNimバヌゞョンのNim / bcon列を远加したした。



ニムたたは錆





NimずRustは優れたパフォヌマンスを期埅しおコンパむルされた蚀語ですが、それらは非垞に異なっおいたす。 私にずっお、それらの類䌌点は次のずおりです。







しかし、それらの違いはより興味深いものです。



哲孊自由たたは芏埋





Nimでプログラミングするず、スクリプト蚀語で曞いおいるような感芚が埗られたす。 圌は本圓に行を消去したす。 Nimは、コヌド内のノむズを可胜な限り取り陀き、それを喜んでプログラミングしようずしたす。



ただし、そのような自由には裏返しがありたす。明確さ、玔床、持続可胜性が損なわれる可胜性がありたす。 小さな䟋を次に瀺したす。Nimのむンポヌトでは、すべおのモゞュヌル名がネヌムスペヌスにむンポヌトされたす。 むンポヌトされたモゞュヌルからの名前は、構文module.symbolを䜿甚するか、名前の制埡されたむンポヌトのためにfrom module import nilを䜿甚しお制限できたすが、これを䜿甚するのは誰ですか さらに、このアプロヌチはNimには䞀般的ではありたせん。 その結果、他の誰かのコヌドたたは自分のコヌドを読み取るずきに、どのモゞュヌルからどの名前が付けられたのかを理解できたせん残念ながら、このような堎合、Nimはパをカツレツから分離するため、名前の競合は発生したせん。



その他の䟋 UFCSでは、 len x 、 len x 、 x.lenたたはx.lenを必芁に応じお䜿甚できたす。 名前をアンダヌスコアず異なる倧文字で区切らないので、 mapWidth 、 mapwidthおよびmap_widthは同じ名前に倉換されたすバヌゞョン0.10.2に「郚分的な倧文字ず小文字の区別」のルヌルが含たれおいお、 Fooずfooは異なる名前ず芋なされたす ; 物事の順序で初期化されおいない倉数の䜿甚です。 理論的には、厳密なコヌディング原則に埓うこずができたすが、Nimでプログラミングするずきは、よりリラックスした気分になりたす。



䞀方、錆は芏埋を尊重したす。 そのコンパむラは非垞に厳栌です。 すべおが非垞に明確でなければなりたせん。 事前に適切なアプロヌチを取埗したす。 あいたいさはRustコヌドに関するものではありたせん...

このアプロヌチは、原則ずしお、長呜のプロゞェクトず持続可胜性に適しおいたすが、Rustでプログラミングを行う堎合は、たったく関心のないような詳现の凊理を開始したす。 タスクの優先事項ではない堎合でも、メモリの䜿甚やパフォヌマンスの向䞊に぀いお考えるようになりたす。 Rustにより、芏埋が向䞊したす。



どちらにも長所ず短所がありたす。 プログラマヌずしお、私はNimをもっず楜しんでいたす。 メンテナヌずしお、Rustで曞かれた補品の方が良いでしょう。



ビゞュアルスタむルPythonたたはC ++





Pythonず同様に、Nimはむンデントを䜿甚しおコヌドブロックを分離し、 文字数を枛らしおいたす 。 RustはC ++に䌌おいたす。 {} 、 :: 、 <> 、およびは、C ++プログラマヌにはおなじみです。さらに、Rustは'aのような新しいものを远加したす。



時々、ニムは文字通りすぎるかもしれたせん。 たずえば、Rustの䞀臎構文を考えたす。



 match key.cmp(&node.key) { Less => return insert(&mut node.left, key, value), Greater => return insert(&mut node.right, key, value), Equal => node.value = value, }
      
      







Nimのcase匏よりもきれいに芋えたす。



 case key of "help", "h": echo usageString of "ignore-case", "i": ignoreCase = true of "o": outFilename = val else: discard
      
      







しかし、党䜓的に、Nimコヌドはノむズが少ないです。 私の意芋では、Rustの特別な混乱は存続期間パラメヌタヌによっお導入され、これは倉曎されたせん。



メモリ管理ガベヌゞコレクタヌたたは手動管理





Nimは安党でないメモリ管理を可胜にし、より予枬可胜な動䜜のためにランタむムでガベヌゞコレクタを管理するためのサポヌトを提䟛したすが。 それはただガベヌゞコレクタヌを備えた蚀語であり、それに長所ず短所がありたす。 Nimのオブゞェクトには、倀のコピヌが割り圓おられたす。 ガベヌゞコレクタヌがタスクに干枉しない堎合、Nimでメモリを管理しおも問題は発生したせん。



Rustはガベヌゞコレクションの限定的なサポヌトを提䟛したすが、倚くの堎合、メモリ管理ではテニュアシステムに䟝存しおいたす。 Rustプログラマヌずしお、初心者にずっお最初の障壁ずなるプログラムを効率的に䜜成する前に、圌のメモリ管理モデル所有暩、借甚、寿呜を完党に理解する必芁がありたす。



䞀方、Rustの匷みはこれにありたす。ガベヌゞコレクタヌを䜿甚しない安党なメモリ管理です。 Rustはこれに぀いお玠晎らしい仕事をしたす。 共有リ゜ヌスのセキュリティ、競合するデヌタアクセスのセキュリティ、およびNULLポむンタヌの削陀に加えお、Rustは実行時のリ゜ヌス消費のオヌバヌヘッドのない非垞に信頌性の高いプログラミング蚀語です。



芁件に応じお、Nimガベヌゞコレクタヌで十分であるか、遞択がRustになりたす。



その他の違い





ニムの匷み



  1. 生産性同じ時間枠で、Nimでより倚くの機胜を利甚できたす
  2. 習埗が簡単
  3. スクリプト蚀語ずしおコンパむルされた蚀語は、タむピング、むンタラクティブな調査、バッチ凊理などに適しおいたす。
  4. チップ

    • メ゜ッドのオヌバヌラむド
    • 新しい挔算子の定矩
    • 名前付き匕数ずデフォルト倀
    • 匷力なマクロ






さびの匷さ



  1. 実際のシステムプログラミング蚀語組み蟌み、ガベヌゞコレクタヌなし、 ハヌドりェアに近い
  2. 安党、芏埋、信頌性
  3. 匷力なカヌネルチヌムず掻発なコミュニティ
  4. チップ

    • 優れたパタヌンマッチングの実装
    • enums 、ただしNimではenumも良い
    • varの代わりにmutをしたしょう 小さいが重芁なこず
    • 構文を逆参照する匷力な構造






゚ラヌ凊理Nimは䞀般的な䟋倖メカニズムを䜿甚し、Rustは戻り倀型Result およびパニックマクロ を䜿甚したす。 私はこれを奜みたせんが、この違いに蚀及するこずが重芁であるこずがわかりたした。



途䞭リリヌス1.0





NimずRustは今幎リリヌスされる予定ですRustはリリヌスされたした  。 これはずおもクヌルです Rustはすでに倚くの泚目を集めおいたすが、ニムはより有名になっおいたす。 味はたったく異なりたすが、どちらも玠晎らしい新しいプログラミング蚀語です。 Rustはパフォヌマンスずセキュリティの点で最善を尜くしたす。 NimはアゞャむルしゃれNimは機敏ですで衚珟力豊かで、スクリプト蚀語ずコンパむル蚀語の長所を実珟しおいたす。 どちらもツヌルキットに远加するのに最適です。



この蚘事を読んだ埌、これらのプログラミング蚀語に぀いお決めたこずを願っおいたす。



All Articles