Macbook M2 上のアンリアル エンジン

Apple プロセッサを搭載した Macbook で Unreal Engine 5 Editor を実行できた場合、これが非常に遅いことに気付いたかもしれません。

エディターとエンジンのパフォーマンスを向上するには、[エンジンのスケーラビリティ設定] -> [中] を設定します。この後、エンジンはあまり美しくない描画を開始しますが、MacBook 上でエンジンを正常に使用できるようになります。

WordPressのモバイルメニューを修正する


document.addEventListener('DOMContentLoaded', function() {
    new navMenu('primary');
    new navMenu('woo');
});

また、Seedlet テーマを使用しているときに iOS/Android で WordPress ブログのブログ メニューを数年間開いていない場合は、以下を追加するだけです。
ファイル wp-content/主題/seedlet/assets/js/primary-navigation.js を関数内で閉じ、デフォルトのサブスクリプション ウィンドウの横にある addEventListener ‘load’ を追加します。

Radio-Maximum-Electron

Radio Maximum Electron は、Windows、Linux、および macOS オペレーティング システムを実行しているコンピュータで Radio Maximum ラジオ局のストリームを聴くように設計された強力で便利なアプリケーションです。このプレーヤーは使いやすさと高機能を兼ね備えており、最小限の労力でライブ ストリーミングにアクセスできます。

GitHub からアプリケーションをダウンロードするだけです。

https://github.com/demensdeum/Radio-Maximum-Electron/releases

著者はRadio Maximumとは何の関係もなく、ただこのラジオが大好きなだけです。
主な機能は Nativifier プロジェクトによって実装されます

https://github.com/nativefier/nativefier

MIT ビルド スクリプトのライセンス。ランタイムには独自のライセンスがあります。

水中シロクマ冒険

ThreeJS を使用して迷路を無限に生成するシンプルなゲームです。

「ファミリーゲーム」をテーマにした3日間のゲームジャム「スタート・ザ・ゲーム」の一環として制作された。

極地探検家の子が母親と一緒に氷の上を歩いていたところ、突然災害が発生しました。氷が割れ、氷海の海に落ちました。お母さんにはクマを救う時間がなく、クマは神秘的な水中洞窟に迷い込んでしまいました。驚いたことに、彼は水中でも呼吸できることに気づきました。この罠から抜け出す方法は 1 つだけです。深海を乗り越え、謎を解き、攻撃的なサメと戦うことです。攻撃的なサメは、狙いを定めてリンゴを投げることで撃退することができます。

彼の目標は、深海の危険を乗り越え、謎を解きながら、この水中の罠から抜け出して母親の元に戻る方法を見つけることです。

https://demensdeum.com/demos/arctica/

Nixy Player

Nixy Player – 小型で拡張可能なクロスプラットフォームの JavaScript ランタイム。

クロスプラットフォーム: Windows、macOS、Linux だけでなく、C++ およびダイナミック ライブラリをサポートする他のプラットフォームでも利用できます。
軽量: リソース消費を最小限に抑え、効率的なパフォーマンスを実現します。
拡張可能: プラグインや追加ライブラリで簡単に拡張できるように設計されています。

最新のリリースとアップデートについては、リリース ページにアクセスしてください。
https://github.com/demensdeum/NixyPlayer/releases/

Raiden Video Ripper

Raiden Video Ripper は、ビデオ編集とフォーマット変換のために設計されたオープンソース プロジェクトです。 Qt 6 (Qt Creator) を使用して構築されており、ビデオをトリミングして MP4、GIF、WebM 形式に変換できます。動画から音声を抽出してMP3形式に変換することもできます。
Интерфейс RaidenVideoRipper

コスタリカからの静止画 4K 60fps HDR (ULTRA HD)
https://www.youtube.com/watch?v=LXb3EKWsInQ
最新のリリースとアップデートについては、リリース ページにアクセスしてください。
https://github.com/demensdeum/RaidenVideoRipper/releases

Donki Hills

1 か月で、Unreal Engine 5 を使用して面白いギャグ、パロディ ゲームを作成しました。開発は Twitch ストリームで行われました。

このゲームのストーリーは、平凡なロシア人男性のジェームスが Tinder でマリアという女の子を見つけたものの、ロシアでの制裁と Tinder の終了により彼女と連絡が取れなくなってしまったというものです。今、彼に残っているのは彼女の写真のスクリーンショットだけであり、グーグルマップを使って写真が撮られた場所、ノボシビルスク近郊のティキエ・ドンキ村を見つけた。ジェームズはマリアを探しにそこへ行くが…。

https://demensdeum.itch.io/donki-hills

ロボットディフェンダー

ソフトウェア機能の正しい動作について議論していると、ユーザーの視点から見た機能が奇妙で非論理的に見える状況に遭遇することがよくあります。プロダクトオーナーとのディスカッションは次のようなものでした。

–ここには明らかに問題行動があります
が。
–まあ、リリースしてユーザーから不満が出始めたら修正します
– ???まあ、わかりました…

それはうまくいく計画のように思えますよね?予算が少なく、納期が厳しく、リサーチが不十分であったり、UI/UX スペシャリストが不足しているチームにとって、かなり最適なアルゴリズムです。何かあったらユーザーからクレームが来るから大丈夫
です。
Google 検索すると、このメソッドのソースは記事「–」から来ていることがわかります。 「苦情主導型開発」 by コーディングホラー

かつて私は、ドクターズ ソーセージなどの食品を 300 ルーブルで販売していました。スーパーマーケットの端末を使って、私はこのソーセージが代金を支払われたことを確信して店を出ました。端末は小切手を印刷しないことを提案し、私はこの小切手で貴重な紙を無駄にしないために同意しました。各製品の商品を「パンチ」するプロセス中に、端末はすべてが正しく機能したことを示すきしむ音を発しました。さらに、音声アラートとともに、端末はバーコード スキャナのバックライトでウィンクしました。

翌日、私は再びスーパーマーケットに食料品を買いに行き、食料品を端末に入れました。出口で私を出迎えたのは、濃いひげを生やした南部風の男性で、スマートフォンを差し出しながらこう言いました。 「カメラに映ってるの?」と彼の携帯を見ると、Arch Enemy の頭蓋骨などが描かれたメロディック・デス・メタルの T シャツを着た自分の姿が見えました。それを疑う理由はありませんでした。
「はい、私です。どうしたんですか?」と男は目を細めて言いました。「昨日はソーセージを殴らなかったね。」…すごい

彼が誰で、どのようにしてこのような結論に至ったのかについて簡単に調査した後、彼は店の天井に掛かっているビデオを見せてくれました。そのビデオでは私がソーセージをパンチしており、端末がスキャナーのバックライトで点滅しています。私はソーセージを袋に入れました。

–スキャナーの動作を動画で
ご紹介します。
–何も役に立たなかったので、ソーセージの代金を支払いましょう!

この態度に少し驚いた私は、端末が正常に動作している兆候がすべて見られたため、ソフトウェアの改善が必要であることを苦情報告書に書いてもらうように依頼しましたが、実際には画面上にそのことを示すことなく、単にバグがあっただけでした。いずれにせよ。

彼と上司との10分間の口論の後、すぐに従業員とクソみたいな端末を擁護しようと駆けつけた上司は、管理者の女の子に電話して苦情の本を持ってきて医師の医者を殴らせることにした。ソーセージ。

その日、私はユーザーがハードウェア製品やソフトウェア製品について苦情を言うことがいかに難しいか、そしておそらく「人々は苦情を言うだろう」というマントラが当てはまっていることに気づきました。修正しましょう」は非常にうまく機能しません。主な理由は、壊れたロボットや壊れたソフトウェア ソリューションを擁護する人々です。わかりやすくするために、私は新しい用語を導入することを提案します。壊れたロボットの擁護者、そして壊れたシステムの擁護者。

一般のユーザーは、ZasRoshniks に邪魔されるため、端末の誤動作について文句を言うことができません。ZasRoshniks は、何らかの理由で操作するマシンに愛着を持ち、おそらくマシンをある種の生命体とみなして愛し始め、何も存在しないことを忘れます。そこに住んでいます。

同様の状況が ZaSSoshniki でも発生します。これらの人々は、ユーザーや他の開発者からの苦情にもかかわらず、フレームワーク、プログラミング言語、またはその他のソフトウェア製品の愚かな欠点を擁護して口から泡を立てることがあります。
ZaSSoshnik との典型的な会話は次のとおりです。

–ここで何かが機能しません。ドキュメントによれば、すべてが正しいようです
–ああ、2005 年のマニュアルを読んでいないのですね。一番下に PROGRAM_START:6969
を追加する必要があると小さな文字で書かれています。
– ???えー

そのような人々は、自分自身がどのようにして自分自身や他の人々の問題、間違い、時間とお金の無駄の拡大に貢献しているのかを理解していない可能性があります。ソフトウェアやハードウェアのソリューションに関する明らかでない事柄や問題が隠蔽されていれば、デジタル変革は不可能であるため、それらのせいで誰もが苦しんでいます。
私は、英国郵便局の Horizo​​n ソフトウェアのバグが人々を借金に追い込み、結婚生活を破壊し、何十年にもわたって人々の生活を台無しにした最近の話を知っています。このすべては、ソフトウェアの問題について沈黙を守り、ソフトウェアを「保護」していた人々の黙認のおかげで続きました。彼。

友人の皆さん、ZaSRoshniks や ZaSSoshniks にならないでください。自分が使用するツールを割り引いて扱いましょう。そうしないと、未来の新しいデジタル世界の人質のように、クソ壊れたシステムに完全に奴隷にされることになります。できない人のために –少なくとも、動作していない干渉するソフトウェア/ハードウェアに注意を払おうとする他の人を困らせないでください。なぜなら、これらの製品の開発者は–に同意しているからです。 「ユーザーが苦情を言い始めたら、私たちはそれを修正します。」

出典
https://blog.codinghorror.com/complaint-driven-development/< /a>
https://habr.com/ru/articles/554404/< br />
https://en.wikipedia.org/wiki/British_Post_Office_scandal

bgfx Emscripten アプリケーションの構築

この投稿では、Emscripten を使用して Web (WebAssembly) 用の bgfx アプリケーションを構築する方法について説明します。

インストール プラットフォームは、Arch Linux などの Linux x86-64 です。

まず、Emscripten バージョン 3.1.51 をインストールしましょう。そうでないと、Emscripten の最新バージョンではダイナミック ライブラリのタイプが変更されているため、成功しません。詳細はこちらでご覧いただけます:
https://github.com/bkaradzic/bgfx/Discussions/3266

これは次のように行われます:


git clone https://github.com/emscripten-core/emsdk.git



cd emsdk



./emsdk install 3.1.51



./emsdk activate 3.1.51



source ./emsdk_env.sh



WebAssembly 用に bgfx をアセンブルしましょう –エムスクリプト:


mkdir bgfx-build-test



cd bgfx-build-test



git clone https://github.com/bkaradzic/bx.git



git clone https://github.com/bkaradzic/bimg.git



git clone https://github.com/bkaradzic/bgfx.git



cd bgfx



emmake make wasm-debug



その結果、.build フォルダーには .bc 拡張子の付いたビットコード ファイルが作成されます。これは、bgfx アプリケーションにリンクする必要があります。
bgfx.bc、bx.bc、bimg.bc である必要があります。アセンブリが異なると、アセンブリのタイプ (リリース/デバッグ) に応じて、これらのファイルの名前も異なります。

.bc ファイルを含む CMakeLists.txt ファイルへのリンク (たとえば、bgfx-experiments プロジェクトのファイルへの絶対パス) を追加します。


target_link_libraries(${PROJECT_NAME} SDL2 GL /home/demensdeum_stream/Sources/bgfx-build/bgfx/.build/wasm/bin/bgfxDebug.bc /home/demensdeum_stream/Sources/bgfx-build/bgfx/.build/wasm/bin/bxDebug.bc /home/demensdeum_stream/Sources/bgfx-build/bgfx/.build/wasm/bin/bimgDebug.bc)



次に、プラットフォーム データのネイティブ ウィンドウ ハンドルを bgfx 初期化に変更します。


bgfx::PlatformData platformData{};



platformData.context = NULL;



platformData.backBuffer = NULL;



platformData.backBufferDS = NULL;



platformData.nwh = (void*)"#canvas";



レンダリング タイプを OpenGL に変更する必要もあります。


bgfx::Init init;



init.type = bgfx::RendererType::OpenGL;







init.resolution.width = screenWidth;



init.resolution.height = screenHeight;



init.resolution.reset = BGFX_RESET_VSYNC;



init.platformData = platformData;







if (!bgfx::init(init))



{



    throw std::runtime_error("Failed to initialize bgfx");



}



GLSL シェーダを 120 に再コンパイルします:


shaderc -f "VertexShader.vs" -o "VertexShader.glsl" --type "v" -p "120"



shaderc -f "FragmentShader.fs" -o "FragmentShader.glsl" --type "f" -p "120"



はい、ただし、.glsl ファイルは –preload-file: として CMakeLists.txt に追加する必要があります:


set(CMAKE_CXX_FLAGS ... <Остальная часть>



--preload-file VertexShader.glsl \



--preload-file FragmentShader.glsl \



アプリケーションのメイン レンダー ループを、emscripten_set_main_loop による while 関数呼び出しに置き換える作業が残ります。

これについてはここで読むことができます:
https ://demensdeum.com/blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/

次に、通常どおり Emscripten プロジェクトをアセンブルします。すべてが機能するはずです。
興味深い – Emscripten 3.1.51 ビルドには OpenAL (または私だけ) が欠けているようです。

bgfx と Emscripten で正しくコンパイルされるプロジェクトのソース コード:
https://github.com/demensdeum/ bgfx-experiments/tree/main/2-emscripten-build

ソース

https://github.com/bkaradzic/bgfx/Discussions/3266
https://bkaradzic.github.io/bgfx/build.html
https://emscripten.org/docs/getting_started/downloads.html
https ://demensdeum.com/blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/
https://llvm.org/docs/BitCodeFormat.html

Surreal Engine C++ を WebAssembly に移植

この投稿では、Surreal Engine ゲーム エンジンを WebAssembly に移植する方法について説明します。

シュール エンジン – Unreal Engine 1 の機能のほとんどを実装するゲーム エンジン、このエンジン上の有名なゲーム -アンリアル トーナメント 99、アンリアル、デウス エクス、アンダイング。これは、主にシングルスレッド実行環境で動作する古典的なエンジンを指します。

当初、私は、妥当な時間内に完了できないプロジェクトに挑戦して、Twitch フォロワーに私でも実行できないプロジェクトがあることを示すことを考えていました。最初のストリーム中に、Emscripten を使用して Surreal Engine C++ を WebAssembly に移植するタスクが実行可能であることに突然気づきました。

Surreal Engine Emscripten Demo

1 か月後には、WebAssembly でフォークとエンジンのアセンブリをデモンストレーションできるようになります。
https://demensdeum.com/demos/SurrealEngine/

オリジナルと同様に、コントロールはキーボードの矢印を使用して実行されます。次に、これをモバイル コントロール (タチ) に適応させ、Unreal Championship 99 レンダリングの正しいライティングやその他のグラフィック機能を追加する予定です。

どこから始めればよいですか?

最初に言いたいのは、Emscripten を使用すると、どのプロジェクトでも C++ から WebAssembly に移植できるということです。唯一の問題は、機能がどの程度完成するかということです。 Surreal Engine の場合は、ライブラリ ポートがすでに Emscripten で利用できるプロジェクトを選択してください。エンジンは SDL 2、OpenAL – ライブラリを使用します。どちらも Emscripten に移植されています。ただし、Vulkan はグラフィックス API として使用されており、現在 HTML5 では利用できません。WebGPU の実装作業が進行中ですが、これもドラフト段階にあり、Vulkan から WebGPU へのさらなる移植がどれほど簡単になるかも不明です。完全に標準化された後。したがって、Surreal Engine 用に独自の基本的な OpenGL-ES / WebGL レンダリングを作成する必要がありました。

プロジェクトのビルド

Surreal Engine でシステムを構築 – CMake も移植を簡素化します。 Emscripten はネイティブ ビルダーを提供します。えむけ、えむけ

Surreal Engine の移植は、Death-Mask と呼ばれる、WebGL/OpenGL ES および C++ で作成した最新ゲームのコードに基づいていました。そのため、開発ははるかに簡単で、必要なビルド フラグとコード サンプルがすべて揃っていました。

CMakeLists.txt の最も重要なポイントの 1 つは Emscripten のビルド フラグです。以下はプロジェクト ファイルの例です。


-s MAX_WEBGL_VERSION=2 \

-s EXCEPTION_DEBUG \

-fexceptions \

--preload-file UnrealTournament/ \

--preload-file SurrealEngine.pk3 \

--bind \

--use-preload-plugins \

-Wall \

-Wextra \

-Werror=return-type \

-s USE_SDL=2 \

-s ASSERTIONS=1 \

-w \

-g4 \

-s DISABLE_EXCEPTION_CATCHING=0 \

-O3 \

--no-heap-copy \

-s ALLOW_MEMORY_GROWTH=1 \

-s EXIT_RUNTIME=1")

ビルド スクリプト自体:


emmake make -j 16

cp SurrealEngine.data /srv/http/SurrealEngine/SurrealEngine.data

cp SurrealEngine.js /srv/http/SurrealEngine/SurrealEngine.js

cp SurrealEngine.wasm /srv/http/SurrealEngine/SurrealEngine.wasm

cp ../buildScripts/Emscripten/index.html /srv/http/SurrealEngine/index.html

cp ../buildScripts/Emscripten/background.png /srv/http/SurrealEngine/background.png

次に、インデックスを準備します。 .html 、プロジェクト ファイル システム プリローダーが含まれています。 Web にアップロードするために、Unreal Championship Demo バージョン 338 を使用しました。CMake ファイルからわかるように、解凍されたゲーム フォルダーがビルド ディレクトリに追加され、Emscripten のプリロード ファイルとしてリンクされました。

メインコードの変更

その後、ゲームのゲーム ループを変更する必要がありました。無限ループを実行することはできません。これによりブラウザがフリーズします。代わりに emscripten_set_main_loop を使用する必要があります。この機能については 2017 年のメモ「< a href="https://demensdeum.com /blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/" rel="noopener" target="_blank">SDL C++ ゲームを HTML5 (Emscripten) に移植」
while ループを終了するコードを if に変更し、ゲーム ループを含むゲーム エンジンのメイン クラスをグローバル スコープに表示し、グローバル オブジェクトからゲーム ループ ステップを呼び出すグローバル関数を作成します。 :


#include <emscripten.h>

Engine *EMSCRIPTEN_GLOBAL_GAME_ENGINE = nullptr;

void emscripten_game_loop_step() {

	EMSCRIPTEN_GLOBAL_GAME_ENGINE->Run();

}

#endif

この後、アプリケーションにバックグラウンド スレッドがないことを確認する必要があります。存在する場合は、シングル スレッド実行用に書き直すか、Emscripten の phtread ライブラリを使用する準備をします。
Surreal Engine のバックグラウンド スレッドは音楽の再生に使用されます。データは、現在のトラック、音楽を再生する必要性、またはその不在に関するメイン エンジン スレッドから取得され、バックグラウンド スレッドはミューテックス経由で新しい状態を受け取り、新しい音楽の再生を開始します。 、または一時停止します。バックグラウンド ストリームは、再生中に音楽をバッファリングするためにも使用されます。
pthread を使用して Emscripten 用の Surreal Engine を構築しようとした試みは失敗しました。SDL2 ポートと OpenAL ポートは pthread サポートなしで構築されており、音楽のために再構築したくなかったためです。そこで、BGM ストリームの機能をループを使用したシングルスレッド実行に移行しました。 C++ コードから pthread 呼び出しを削除することで、バッファリングと音楽再生をメイン スレッドに移動し、遅延が発生しないように、バッファを数秒増やしました。

次に、グラフィックとサウンドの具体的な実装について説明します。

Vulkan はサポートされていません!

はい、Vulkan は HTML5 ではサポートされていませんが、すべてのマーケティング パンフレットでは Vulkan の主な利点としてクロスプラットフォームおよび広範なプラットフォームのサポートが紹介されています。このため、簡素化された OpenGL タイプの基本的なグラフィックス レンダラーを独自に作成する必要がありました。 ES はモバイル デバイスで使用され、最新の OpenGL のファッショナブルな機能が含まれていない場合もありますが、WebGL に非常によく移植されており、まさに Emscripten が実装しているものです。基本的なタイル レンダリング、最も単純な GUI 表示用の bsp レンダリング、およびモデルとマップのレンダリングの作成は 2 週間で完了しました。おそらくこれがこのプロジェクトで最も困難な部分でした。 Surreal Engine レンダリングの全機能を実装するには、まだ多くの作業が必要です。そのため、読者からのコードやプル リクエストの形での支援を歓迎します。

OpenAL がサポートされています!

幸運なことに、Surreal Engine はオーディオ出力に OpenAL を使用しています。 OpenAL で簡単な Hello World を記述し、Emscripten を使用して WebAssembly で組み立てたので、すべてがいかに単純であるかが明らかになり、サウンドの移植に着手しました。
数時間のデバッグ後、Emscripten の OpenAL 実装にはいくつかのバグがあることが明らかになりました。たとえば、モノラル チャネル数の読み取りを初期化するときに、メソッドが無限の数を返し、無限サイズのベクトルを初期化しようとした後、C++例外vector::length_errorでクラッシュします。
>
モノラル チャンネルの数を 2048 にハードコーディングすることで、この問題を回避することができました。


		alcGetIntegerv(alDevice, ALC_STEREO_SOURCES, 1, &stereoSources);



#if __EMSCRIPTEN__

		monoSources = 2048; // for some reason Emscripten's OpenAL gives infinite monoSources count, bug?

#endif



ネットワークはありますか?

Surreal Engine は現在オンライン プレイをサポートしていません。ボットでのプレイはサポートされていますが、これらのボット用の AI を作成する人が必要です。理論的には、Websocket を使用して WebAssembly/Emscripten にネットワーク ゲームを実装できます。

結論

結論として、Emscripten ポートがあるライブラリの使用と、WebAssembly 用に C++ でゲームを実装した過去の経験のおかげで、Surreal Engine の移植は非常にスムーズに完了したと言いたいと思います。エムスクリプテンで。以下は、このトピックに関する知識源とリポジトリへのリンクです。
マ、マ、マ、モンスターを倒せ!

また、できれば WebGL/OpenGL ES レンダリング コードでプロジェクトを支援したい場合は、Telegram で私に連絡してください。
https://t.me/demenscave

リンク

https://demensdeum.com/demos/Sur​​realEngine/
https://github.com/demensdeum/SurrealEngine-Emscripten

https://github.com/dpjudas/SurrealEngine

Flash Forever – Interceptor 2021

Recently, it turned out that Adobe Flash works quite stably under Wine. During a 4-hour stream, I made the game Interceptor 2021, which is a sequel to the game Interceptor 2020, written for the ZX Spectrum.

For those who are not in the know – the Flash technology provided interactivity on the web from 2000 to around 2015. Its shutdown was prompted by an open letter from Steve Jobs, in which he wrote that Flash should be consigned to history because it lagged on the iPhone. Since then, JS has become even more sluggish than Flash, and Flash itself has been wrapped in JS, making it possible to run it on anything thanks to the Ruffle player.

You can play it here:
https://demensdeum.com/demos/Interceptor2021

Video:
https://www.youtube.com/watch?v=-3b5PkBvHQk

Source code:
https://github.com/demensdeum/Interceptor-2021

メイソンズ-DR デモ ゲーム

Masonry-AR は、現実世界で街を移動し、本からフリーメーソンの知識を収集し、通貨を入手してフリーメーソン教団の領土を占領する必要がある拡張現実ゲームです。このゲームは実際の組織とは何の関係もありません。すべての試合はランダムです。

ゲームデモ:
https://demensdeum.com/demos/masonry-ar/client

ヴィッキー:
https://demensdeum.com/masonry-ar-wiki-ru/

ソースコード:
https://github.com/demensdeum/Masonry-AR

CRUDリポジトリ

このノートでは、よく知られている古典的な CRUD パターン、Swift 言語での実装の基本原則について説明します。 Swift は、Windows、Linux、macOS、iOS、Android で利用できるオープンなクロスプラットフォーム言語です。

データ ストレージとアプリケーション ロジックを抽象化するためのソリューションは数多くあります。そのようなソリューションの 1 つが CRUD アプローチです。これは C– の頭字語です。作成、R -読み取り、U –最新情報、D–削除
します。通常、この原則はデータベースへのインターフェイスの実装を通じて実装され、要素は id などの一意の識別子を使用して操作されます。 CRUD – 文字ごとにインターフェイスが作成されます。作成(オブジェクト、ID)、読み取り(ID)、更新(オブジェクト、ID)、削除(オブジェクト、ID)。
オブジェクトの内部に ID が含まれている場合、オブジェクト全体がフィールド – とともに渡されるため、メソッド (Create、Update、Delete) から id 引数を省略できます。 ID。しかし、– についてはID によってデータベースからオブジェクトを取得したいため、読み取りには ID が必要です。

名前はすべて架空のものです

仮想の AssistantAI アプリケーションが無料の EtherRelm データベース SDK を使用して作成され、統合は簡単で、API は非常に便利で、その結果、アプリケーションが市場にリリースされたと想像してみましょう。
突然、SDK 開発者の EtherRelm は有料化を決定し、アプリケーション ユーザーあたりの価格を年間 100 ドルに設定しました。
何?はい! AssistantAI の開発者は、すでに 100 万人のアクティブ ユーザーを抱えているため、今何をすべきでしょうか。 1億ドル支払う?
代わりに、プラットフォーム固有の RootData データベースへのアプリケーションの転送を評価する決定が行われます。プログラマーによると、そのような転送には約 6 か月かかりますが、これにはアプリケーションの新機能の実装は考慮されていません。いくつか考えた結果、アプリケーションを市場から削除し、BueMS データベースが組み込まれた別の無料のクロスプラットフォーム フレームワークに書き直すことが決定されました。これにより、有料データベースの問題が解決され、他のプラットフォームでの開発が簡素化されます。
1 年後、アプリケーションは BueMS で書き直されましたが、突然フレームワーク開発者が有料化を決定しました。チームは同じ罠に 2 度陥ったことが判明しました。2 度目から抜け出せるかどうかはまったく別の話です。

抽象化が助けになります

開発者がアプリケーション内でインターフェイスの抽象化を使用していれば、これらの問題は回避できたはずです。 OOPの3つの柱へ–ポリモーフィズム、カプセル化、継承、少し前に別の – が追加されました。抽象化
します。データ抽象化により、ビジネス上の問題を解決するために使用される特定の実装を実装するのに十分な正確さを保ちながら、アイデアやモデルを最小限の詳細で一般的な用語で記述することができます。
アプリケーションロジックが依存しないようにデータベース操作を抽象化するにはどうすればよいでしょうか?私たちは CRUD アプローチを使用しています!

簡略化された UML CRUD 図は次のようになります。

架空の EtherRelm データベースの例:

実際の SQLite データベースの例:

すでにお気づきのとおり、データベースを切り替えると、アプリケーションがやり取りする CRUD インターフェイスのみが変更され、変更されません。 CRUD は GoF パターンの実装です。アダプターなので、これを使用して、アプリケーション インターフェイスをあらゆるデータベースに適合させ、互換性のないインターフェイスを結合します。
言葉は空です、コードを見せてください
プログラミング言語で抽象化を実装するには、インターフェイス/プロトコル/抽象クラスが使用されます。これらはすべて同じ程度の現象ですが、インタビュー中にそれらの違いを挙げるように求められることがありますが、個人的にはこれはあまり意味がないと考えています。使用の唯一の目的はデータ抽象化を実装することです。それ以外の場合は、インタビュー対象者の記憶をテストすることです。
CRUD は多くの場合、リポジトリ パターンのフレームワーク内で実装されますが、リポジトリは CRUD インターフェイスを実装する場合と実装しない場合があり、すべて開発者の創意工夫に依存します。

UserDefaults データベースを直接操作する、Book 構造リポジトリの非常に典型的な Swift コードを考えてみましょう。


struct Book: Codable {
	let title: String
	let author: String
}

class BookRepository {
	func save(book: Book) {
    		let record = try! JSONEncoder().encode(book)
    		UserDefaults.standard.set(record, forKey: book.title)
	}
    
	func get(bookWithTitle title: String) -> Book? {
    		guard let data = UserDefaults.standard.data(forKey: title) else { return nil }
    		let book = try! JSONDecoder().decode(Book.self, from: data)
    		return book
	}
    
	func delete(book: Book) {
    		UserDefaults.standard.removeObject(forKey: book.title)
	}
}

let book = Book(title: "Fear and Loathing in COBOL", author: "Sir Edsger ZX Spectrum")
let repository = BookRepository()
repository.save(book: book)
print(repository.get(bookWithTitle: book.title)!)
repository.delete(book: book)
guard repository.get(bookWithTitle: book.title) == nil else {
	print("Error: can't delete Book from repository!")
	exit(1)
}

上記のコードは単純に見えますが、DRY (Do notrepeat Yourself) 原則の違反の数とコードの一貫性を数えてみましょう。
UserDefaults データベースへの接続
JSON エンコーダーおよびデコーダーとの接続 – #8211; JSONエンコーダ、JSONデコーダ
Book 構造に接続されていますが、データベースに保存する構造ごとにリポジトリ クラスを作成しないように抽象リポジトリが必要です (DRY 違反)

この種の CRUD リポジトリ コードはよく見かけます。使用することはできますが、コードの高度な結合と重複により、時間の経過とともにそのサポートが非常に複雑になるという事実につながります。これは、別のデータベースに切り替えようとする場合、またはアプリケーションで作成されたすべてのリポジトリでデータベースを操作する内部ロジックを変更する場合に特に顕著になります。
コードを複製する代わりに、カップリングを高く保ちます。 CRUD リポジトリのプロトコルを作成して、データベース インターフェイスとアプリケーション ビジネス ロジックを抽象化し、DRY を尊重し、低結合を実装しましょう。

    typealias Item = Codable
    typealias ItemIdentifier = String
    
    func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws
    func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T
    func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws
    func delete(id: CRUDRepository.ItemIdentifier) async throws
}

CRUDRepository プロトコルは、特定の CRUD リポジトリをさらに実装するためのインターフェイスと関連するデータ型を記述します。

次に、UserDefaults データベースの特定の実装を作成します。

    private typealias RecordIdentifier = String
    
    let tableName: String
    let dataTransformer: DataTransformer
    
    init(
   	 tableName: String = "",
   	 dataTransformer: DataTransformer = JSONDataTransformer()
    ) {
   	 self.tableName = tableName
   	 self.dataTransformer = dataTransformer
    }
    
    private func key(id: CRUDRepository.ItemIdentifier) -> RecordIdentifier {
   	 "database_\(tableName)_item_\(id)"
    }
   	 
    private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
   	 UserDefaults.standard.data(forKey: key(id: id)) != nil
    }
    
    func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 let data = try await dataTransformer.encode(item)
   	 UserDefaults.standard.set(data, forKey: key(id: id))
   	 UserDefaults.standard.synchronize()
    }
    
    func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
   	 guard let data = UserDefaults.standard.data(forKey: key(id: id)) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let item: T = try await dataTransformer.decode(data: data)
   	 return item
    }
    
    func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let data = try await dataTransformer.encode(item)
   	 UserDefaults.standard.set(data, forKey: key(id: id))
   	 UserDefaults.standard.synchronize()
    }
    
    func delete(id: CRUDRepository.ItemIdentifier) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 UserDefaults.standard.removeObject(forKey: key(id: id))
   	 UserDefaults.standard.synchronize()
    }
}

コードは長く見えますが、疎結合を含む CRUD リポジトリの完全な具体的な実装が含まれています。詳細は以下で説明します。
コードの自己文書化のために typealias が追加されました。
弱い結合と強い結合
について。特定の構造 (struct) からの切り離しは、汎用 T を使用して実装されます。これには、Codable プロトコルを実装する必要があります。 Codable では、TopLevelEncoder および TopLevelDecoder プロトコルを実装するクラス (JSONEncoder や JSONDecoder など) を使用して構造を変換できます。基本型 (Int、String、Float など) を使用する場合、構造を変換するために追加のコードを記述する必要はありません。 p>

特定のエンコーダとデコーダからの分離は、DataTransformer プロトコルの抽象化を使用して行われます。

	func encode<T: Encodable>(_ object: T) async throws -> Data
	func decode<T: Decodable>(data: Data) async throws -> T
}

データ トランスフォーマーの実装を使用して、エンコーダーとデコーダーのインターフェイスの抽象化を実装し、さまざまなタイプのデータ形式で確実に動作するように疎結合を実装しました。

以下は、特定の DataTransformer、つまり JSON のコードです。

	func encode<T>(_ object: T) async throws -> Data where T : Encodable {
    		let data = try JSONEncoder().encode(object)
    		return data
	}
    
	func decode<T>(data: Data) async throws -> T where T : Decodable {
    		let item: T = try JSONDecoder().decode(T.self, from: data)
    		return item
	}
}

それは可能でしたか?

何が変わりましたか?今後は、Codable プロトコルを実装する構造を操作できるように特定のリポジトリを初期化するだけで十分です。そのため、コードを複製する必要がなく、アプリケーションの疎結合を実装できます。

特定のリポジトリを使用したクライアント CRUD の例。UserDefaults はデータベース、JSON データ形式、クライアント構造であり、配列の書き込みと読み取りの例でもあります。


print("One item access example")

do {
	let clientRecordIdentifier = "client"
	let clientOne = Client(name: "Chill Client")
	let repository = UserDefaultsRepository(
    	tableName: "Clients Database",
    	dataTransformer: JSONDataTransformer()
	)
	try await repository.create(id: clientRecordIdentifier, item: clientOne)
	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print("Client Name: \(clientRecord.name)")
	clientRecord.name = "Busy Client"
	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
	print("Updated Client Name: \(updatedClient.name)")
	try await repository.delete(id: clientRecordIdentifier)
	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print(removedClientRecord)
}
catch {
	print(error.localizedDescription)
}

print("Array access example")

let clientArrayRecordIdentifier = "clientArray"
let clientOne = Client(name: "Chill Client")
let repository = UserDefaultsRepository(
	tableName: "Clients Database",
	dataTransformer: JSONDataTransformer()
)
let array = [clientOne]
try await repository.create(id: clientArrayRecordIdentifier, item: array)
let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
print(savedArray.first!)

最初の CRUD チェック中に例外処理が実装されており、リモート アイテムの読み取りは利用できなくなります。

データベースの切り替え

次に、現在のコードを別のデータベースに転送する方法を説明します。たとえば、ChatGPT が生成した SQLite リポジトリ コードを取り上げます。


class SQLiteRepository: CRUDRepository {
    private typealias RecordIdentifier = String
    
    let tableName: String
    let dataTransformer: DataTransformer
    private var db: OpaquePointer?

    init(
   	 tableName: String,
   	 dataTransformer: DataTransformer = JSONDataTransformer()
    ) {
   	 self.tableName = tableName
   	 self.dataTransformer = dataTransformer
   	 self.db = openDatabase()
   	 createTableIfNeeded()
    }
    
    private func openDatabase() -> OpaquePointer? {
   	 var db: OpaquePointer? = nil
   	 let fileURL = try! FileManager.default
   		 .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
   		 .appendingPathComponent("\(tableName).sqlite")
   	 if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
   		 print("error opening database")
   		 return nil
   	 }
   	 return db
    }
    
    private func createTableIfNeeded() {
   	 let createTableString = """
   	 CREATE TABLE IF NOT EXISTS \(tableName) (
   	 id TEXT PRIMARY KEY NOT NULL,
   	 data BLOB NOT NULL
   	 );
   	 """
   	 var createTableStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
   		 if sqlite3_step(createTableStatement) == SQLITE_DONE {
       		 print("\(tableName) table created.")
   		 } else {
       		 print("\(tableName) table could not be created.")
   		 }
   	 } else {
   		 print("CREATE TABLE statement could not be prepared.")
   	 }
   	 sqlite3_finalize(createTableStatement)
    }
    
    private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
   	 let queryStatementString = "SELECT data FROM \(tableName) WHERE id = ?;"
   	 var queryStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
   		 sqlite3_bind_text(queryStatement, 1, id, -1, nil)
   		 if sqlite3_step(queryStatement) == SQLITE_ROW {
       		 sqlite3_finalize(queryStatement)
       		 return true
   		 } else {
       		 sqlite3_finalize(queryStatement)
       		 return false
   		 }
   	 } else {
   		 print("SELECT statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
    }
    
    func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 let insertStatementString = "INSERT INTO \(tableName) (id, data) VALUES (?, ?);"
   	 var insertStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
   		 let data = try await dataTransformer.encode(item)
   		 sqlite3_bind_text(insertStatement, 1, id, -1, nil)
   		 sqlite3_bind_blob(insertStatement, 2, (data as NSData).bytes, Int32(data.count), nil)
   		 if sqlite3_step(insertStatement) == SQLITE_DONE {
       		 print("Successfully inserted row.")
   		 } else {
       		 print("Could not insert row.")
       		 throw CRUDRepositoryError.databaseError
   		 }
   	 } else {
   		 print("INSERT statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(insertStatement)
    }
    
    func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
   	 let queryStatementString = "SELECT data FROM \(tableName) WHERE id = ?;"
   	 var queryStatement: OpaquePointer? = nil
   	 var item: T?
   	 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
   		 sqlite3_bind_text(queryStatement, 1, id, -1, nil)
   		 if sqlite3_step(queryStatement) == SQLITE_ROW {
       		 let queryResultCol1 = sqlite3_column_blob(queryStatement, 0)
       		 let queryResultCol1Length = sqlite3_column_bytes(queryStatement, 0)
       		 let data = Data(bytes: queryResultCol1, count: Int(queryResultCol1Length))
       		 item = try await dataTransformer.decode(data: data)
   		 } else {
       		 throw CRUDRepositoryError.recordNotFound(id: id)
   		 }
   	 } else {
   		 print("SELECT statement could not be prepared")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(queryStatement)
   	 return item!
    }
    
    func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let updateStatementString = "UPDATE \(tableName) SET data = ? WHERE id = ?;"
   	 var updateStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
   		 let data = try await dataTransformer.encode(item)
   		 sqlite3_bind_blob(updateStatement, 1, (data as NSData).bytes, Int32(data.count), nil)
   		 sqlite3_bind_text(updateStatement, 2, id, -1, nil)
   		 if sqlite3_step(updateStatement) == SQLITE_DONE {
       		 print("Successfully updated row.")
   		 } else {
       		 print("Could not update row.")
       		 throw CRUDRepositoryError.databaseError
   		 }
   	 } else {
   		 print("UPDATE statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(updateStatement)
    }
    
    func delete(id: CRUDRepository.ItemIdentifier) async throws {
   	 guard try await isExists(id: id) else {
   		 throw CRUDRepositoryError.recordNotFound(id: id)
   	 }
   	 let deleteStatementString = "DELETE FROM \(tableName) WHERE id = ?;"
   	 var deleteStatement: OpaquePointer? = nil
   	 if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
   		 sqlite3_bind_text(deleteStatement, 1, id, -1, nil)
   		 if sqlite3_step(deleteStatement) == SQLITE_DONE {
       		 print("Successfully deleted row.")
   		 } else {
       		 print("Could not delete row.")
       		 throw CRUDRepositoryError.databaseError
   		 }
   	 } else {
   		 print("DELETE statement could not be prepared.")
   		 throw CRUDRepositoryError.databaseError
   	 }
   	 sqlite3_finalize(deleteStatement)
    }
}

または、同じく ChatGPT によって生成された、ファイル システムのリポジトリの CRUD コード:


class FileSystemRepository: CRUDRepository {
	private typealias RecordIdentifier = String
    
	let directoryName: String
	let dataTransformer: DataTransformer
	private let fileManager = FileManager.default
	private var directoryURL: URL
    
	init(
    	directoryName: String = "Database",
    	dataTransformer: DataTransformer = JSONDataTransformer()
	) {
    	self.directoryName = directoryName
    	self.dataTransformer = dataTransformer
   	 
    	let paths = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
    	directoryURL = paths.first!.appendingPathComponent(directoryName)
   	 
    	if !fileManager.fileExists(atPath: directoryURL.path) {
        	try? fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
    	}
	}
    
	private func fileURL(id: CRUDRepository.ItemIdentifier) -> URL {
    	return directoryURL.appendingPathComponent("item_\(id).json")
	}
    
	private func isExists(id: CRUDRepository.ItemIdentifier) async throws -> Bool {
    	return fileManager.fileExists(atPath: fileURL(id: id).path)
	}
    
	func create<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
    	let data = try await dataTransformer.encode(item)
    	let url = fileURL(id: id)
    	try data.write(to: url)
	}
    
	func read<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier) async throws -> T {
    	let url = fileURL(id: id)
    	guard let data = fileManager.contents(atPath: url.path) else {
        	throw CRUDRepositoryError.recordNotFound(id: id)
    	}
    	let item: T = try await dataTransformer.decode(data: data)
    	return item
	}
    
	func update<T: CRUDRepository.Item>(id: CRUDRepository.ItemIdentifier, item: T) async throws {
    	guard try await isExists(id: id) else {
        	throw CRUDRepositoryError.recordNotFound(id: id)
    	}
    	let data = try await dataTransformer.encode(item)
    	let url = fileURL(id: id)
    	try data.write(to: url)
	}
    
	func delete(id: CRUDRepository.ItemIdentifier) async throws {
    	guard try await isExists(id: id) else {
        	throw CRUDRepositoryError.recordNotFound(id: id)
    	}
    	let url = fileURL(id: id)
    	try fileManager.removeItem(at: url)
	}
}

クライアント コード内のリポジトリを置き換えます。


print("One item access example")

do {
	let clientRecordIdentifier = "client"
	let clientOne = Client(name: "Chill Client")
	let repository = FileSystemRepository(
    	directoryName: "Clients Database",
    	dataTransformer: JSONDataTransformer()
	)
	try await repository.create(id: clientRecordIdentifier, item: clientOne)
	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print("Client Name: \(clientRecord.name)")
	clientRecord.name = "Busy Client"
	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
	print("Updated Client Name: \(updatedClient.name)")
	try await repository.delete(id: clientRecordIdentifier)
	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print(removedClientRecord)
}
catch {
	print(error.localizedDescription)
}

print("Array access example")

let clientArrayRecordIdentifier = "clientArray"
let clientOne = Client(name: "Chill Client")
let repository = FileSystemRepository(
	directoryName: "Clients Database",
	dataTransformer: JSONDataTransformer()
)
let array = [clientOne]
try await repository.create(id: clientArrayRecordIdentifier, item: array)
let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
print(savedArray.first!)

UserDefaultsRepository の初期化は、適切な引数を備えた FileSystemRepository に置き換えられました。
クライアント コードの 2 番目のバージョンを実行すると、ドキュメント フォルダーに「Clients Database」ディレクトリが表示されます。このディレクトリには、1 つのクライアント構造を持つ JSON でシリアル化された配列のファイルが含まれています。

データ保存形式の切り替え

次に、ChatGPT に XML のエンコーダーとデコーダーを生成するよう依頼しましょう。

	let formatExtension = "xml"
    
	func encode<T: Encodable>(_ item: T) async throws -> Data {
    	let encoder = PropertyListEncoder()
    	encoder.outputFormat = .xml
    	return try encoder.encode(item)
	}
    
	func decode<T: Decodable>(data: Data) async throws -> T {
    	let decoder = PropertyListDecoder()
    	return try decoder.decode(T.self, from: data)
	}
}

Swift の組み込み型のおかげで、ニューラル ネットワークのタスクが初歩的になります。

クライアント コードで JSON を XML に置き換えます。


print("One item access example")

do {
	let clientRecordIdentifier = "client"
	let clientOne = Client(name: "Chill Client")
	let repository = FileSystemRepository(
    	directoryName: "Clients Database",
    	dataTransformer: XMLDataTransformer()
	)
	try await repository.create(id: clientRecordIdentifier, item: clientOne)
	var clientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print("Client Name: \(clientRecord.name)")
	clientRecord.name = "Busy Client"
	try await repository.update(id: clientRecordIdentifier, item: clientRecord)
	let updatedClient: Client = try await repository.read(id: clientRecordIdentifier)
	print("Updated Client Name: \(updatedClient.name)")
	try await repository.delete(id: clientRecordIdentifier)
	let removedClientRecord: Client = try await repository.read(id: clientRecordIdentifier)
	print(removedClientRecord)
}
catch {
	print(error.localizedDescription)
}

print("Array access example")

let clientArrayRecordIdentifier = "clientArray"
let clientOne = Client(name: "Chill Client")
let repository = FileSystemRepository(
	directoryName: "Clients Database",
	dataTransformer: XMLDataTransformer()
)
let array = [clientOne]
try await repository.create(id: clientArrayRecordIdentifier, item: array)
let savedArray: [Client] = try await repository.read(id: clientArrayRecordIdentifier)
print(savedArray.first!)

クライアント コードは 1 つの式 JSONDataTransformer -> のみに変更されました。 XMLDataTransformer

合計

CRUD リポジトリは、アプリケーション アーキテクチャ コンポーネントの疎結合を実装するために使用できる設計パターンの 1 つです。もう 1 つの解決策 – ORM (Object-Relational Mapping) を使用すると、ORM は構造をデータベースに完全にマッピングし、モデルによる変更をデータベース上に表示 (マッピング (!)) するアプローチを使用します。
しかし、それはまったく別の話です。

Swift 用の CRUD リポジトリの完全な実装は、次の場所から入手できます。
https://gitlab.com/demensdeum/crud-example

ところで、Swift は macOS 以外でも長い間サポートされてきましたが、この記事のコードはすべて Arch Linux 上で作成され、テストされました。

ソース

https://developer.apple.com/documentation/combine/topleveldecoder
https://developer.apple.com/documentation/combine/toplevelencoder
https://en.wikipedia.org/wiki/Create,_read,_update_and_delete

dd 入出力エラー

Linux で dd を使用して通常のディスクをコピーするときに入出力エラーが発生した場合はどうすればよいですか?

シトゥエヴィナは非常に悲しいですが、解決可能です。おそらく、使用、書き込み、読み取りができなくなった不良ブロックを含む故障したディスクを扱っていると考えられます。

そのようなディスクは必ず S.M.A.R.T. を使用してチェックしてください。ディスク エラーが表示される可能性が高くなります。私の場合はこれに当てはまりました。不良ブロックの数が非常に多かったので、古いハードドライブに別れを告げて、新しい SSD に交換する必要がありました。

問題は、このディスクには、作業に必要なライセンスされたソフトウェアを備えた完全に動作するシステムが含まれていることです。 partimage を使用してデータをすばやくコピーしようとしましたが、ユーティリティがディスクの 3 分の 1 だけをコピーし、セグメンテーション違反かその他の面白い Sishny/Sipplusplus ジョークで終了することに突然気づきました。

次に、ddを使用してデータをコピーしようとしましたが、ddがpartimageとほぼ同じ場所に到達し、入出力エラーが発生することがわかりました。同時に、conv=noerr、skip などのあらゆる種類の面白いフラグはまったく役に立ちませんでした。

しかし、ddrescue という GNU ユーティリティを使用すると、データは問題なく別のディスクにコピーされました。

После этого мои волосы стали шелковистыми, вернулась жена, дети и собака перестала кусать диван.

Большим плюсом ddrescue является наличие встроенного прогрессбара, поэтому не приходится костылять какие-то ухищрения навроде pv и всяких не особо красивых флажков dd. Также ddrescure показывает количество попыток прочитать данные; еще на вики написано что утилита обладает каким-то сверх алгоритмом для считывания поврежденных данных, оставим это на проверку людям которые любят ковыряться в исходниках, мы же не из этих да?

https://ru.wikipedia.org/wiki/Ddrescue
https://www.gnu.org/software/ddrescue/ddrescue_ru.html

チャットGPT

皆さん、こんにちは!この記事では、ChatGPT – について話したいと思います。 OpenAI の強力な言語モデリングは、テキスト処理のさまざまな問題の解決に役立ちます。このツールがどのように機能し、実際の状況でどのように使用できるかを説明します。始めましょう!

現時点では、ChatGPT はニューラル ネットワークに基づく世界最高の言語モデルの 1 つです。これは、開発者が自然言語を生成し、その中で人々とコミュニケーションできるインテリジェント システムを作成できるように支援することを目的として作成されました。

ChatGPT の主な利点の 1 つは、テキストを状況に応じてモデル化できることです。これは、モデルが以前の対話を考慮し、それを使用して状況をより正確に理解し、より自然な応答を生成することを意味します。

ChatGPT は、カスタマー サポートの自動化、チャットボットの作成、テキスト生成など、さまざまなタスクに使用できます。

ChatGPT の背後にあるニューラル ネットワークは、非常に正確な予測を保証するために、大量のテキストでトレーニングされました。これにより、モデルは対話をサポートし、質問に答えることができる自然なテキストを生成できるようになります。

ChatGPT を使用すると、自然言語で人々と対話できる独自のチャットボットやその他のインテリジェント システムを作成できます。これは、観光、小売、カスタマー サポートなどの業界で特に役立ちます。

結論として、ChatGPT –これは、さまざまな言語モデリングの問題を解決するための強力なツールです。コンテキスト モデリングの機能により、チャットボットやインテリジェント システムの作成に特に役立ちます。


実際、ChatGPT は上記のすべてを完全に単独で記述しました。何?はい?私自身もショックを受けています!

ここでグリッド自体を試すことができます。
https://chat.openai.com/chat

macOS で USB キーボードのバックライトをオンにする

最近、RGB バックライト付きの非常に安価な Getorix GK-45X USB キーボードを購入しました。 M1 プロセッサを搭載した MacBook Pro に接続してみたところ、RGB バックライトが機能しないことが明らかになりました。 Fn + Scroll Lock の魔法の組み合わせを押してもバックライトは点灯せず、MacBook 画面のバックライト レベルのみが変化しました。
この問題にはいくつかの解決策があります。OpenRGB (機能しない)、HID LED テスト (機能しない) です。 kvmswitch ユーティリティのみが機能しました:
https://github.com/stoutput/OSX-KVM

GitHub からダウンロードし、システム設定のセキュリティ パネルでターミナルからの実行を許可する必要があります。
説明からわかるように、ユーティリティを起動すると、Fn + Scroll Lock キーが送信され、キーボードのバックライトがオン/オフになります。

みつめが通る (NES) –ダンディの第三の目

https://www.youtube.com/watch?v=LT2U3CJnzxU

みつめが通る (三つ目とおる みつめが通る?、文字通り「three-eyed」) は、1992 年に夏目によって Nintendo Entertainment System 専用に開発されたプラットフォーム ビデオ ゲームです。このゲームは漫画およびアニメ『ミツメが通る』を原作としています。これは、ナツメによって開発された 2 番目のアニメベースのゲームです。前作は 2 年前に MSX プラットフォーム向けにリリースされた『みつめが通る: 3Lie-Mon』でした。ロシアでは、このゲームは「3 Eyes」または「3 Eyes」としてよく知られています。第三の目。

Number 2

Comrades, I take pride in projects that were created on the basis of Flame Steel Framework 1 and specifically on Flame Steel Engine 1, namely Death-Mask, Cube Art Project, since all this was conceived as a big experiment, creating a multimedia framework alone that can work on the most platforms. I think the experiment ended successfully immediately after the release of the Cube Art Project.

Now about the decisions that I came to during the development of new projects on FSFramework 1

During the development of Space Jaguar and the Space Jaguar Galaxy Bastards shooter, it became clear that the Flame Steel Framework tools were already outdated, not even having time to become at least somewhat convenient.

Therefore, I decided to develop a completely new Flame Steel Framework 2. The main decision will be to switch to my Rise 2 transpiler language, and the Component System (ECS) will no longer be used architecturally, because. it turned out to be needed only within the framework of game logic with great dynamics. For this reason, in Flame Steel Framework 2, the component system will only be possible while using the scripting languages ​​that are planned to be implemented (at least Lua and JavaScript), an interesting feature is that these languages ​​​​are dynamic in nature, so additional creation of the component system is redundant.

You can follow the development of new projects on the blog and on Gitlab:

https://gitlab.com/demensdeum/rise2

https://gitlab.com/demensdeum/flamesteelengine2

https://gitlab.com/demensdeum/flame-steel-engine-2-demo-projects

https://gitlab.com/demensdeum/space-jaguar-action-rpg

https://gitlab.com/demensdeum/space-jaguar-galaxy-bastards

ツリーソート

ツリーソート – 二分探索ツリーを使用したソート。時間計算量 – O(n²)。このようなツリーでは、左側の各ノードにはノードより小さい番号があり、右側にはノードより大きい番号があります。ルートから来て左から右に値を出力すると、ソートされた番号のリストが得られます。 。驚くべきことですね?

二分探索ツリー図を考えてみましょう。

デリック・クッツェー (パブリック ドメイン)

左下の各ノード、つまり右側のノードごとに、左下隅の最後から 2 番目の左ノードから数字を手動で読み取ってみてください。

次のようになります:

<オル>

  • 左下の最後から2番目のノード – 3.
  • 左分岐があります – 1.
  • この番号 (1) を選択してください
  • 次に頂点 3 (1, 3) を取得します
  • 右側はブランチ 6 ですが、ブランチが含まれています。したがって、同じように読みます。
  • ノード 6 の左分岐 4 (1、3、4)
  • ノード自体は 6 (1、3、4、6) です
  • 右 7 (1、3、4、6、7)
  • ルート ノードに移動します – 8 (1、3、4、6、7、8)
  • 類推により、右側にすべてを出力します
  • 最終的なリストを取得します – 1、3、4、6、7、8、10、13、14
  • コードでアルゴリズムを実装するには、次の 2 つの関数が必要です。

    <オル>

  • 二分探索ツリーの組み立て
  • 二分探索ツリーを正しい順序で出力する
  • 二分探索木は読み取られたときと同じ方法で組み立てられ、それが大きいか小さいかに応じて、左側または右側の各ノードに番号が付けられます。

    Lua での例:

    
    function Node:new(value, lhs, rhs)
        output = {}
        setmetatable(output, self)
        self.__index = self  
        output.value = value
        output.lhs = lhs
        output.rhs = rhs
        output.counter = 1
        return output  
    end
    
    function Node:Increment()
        self.counter = self.counter + 1
    end
    
    function Node:Insert(value)
        if self.lhs ~= nil and self.lhs.value > value then
            self.lhs:Insert(value)
            return
        end
    
        if self.rhs ~= nil and self.rhs.value < value then
            self.rhs:Insert(value)
            return
        end
    
        if self.value == value then
            self:Increment()
            return
        elseif self.value > value then
            if self.lhs == nil then
                self.lhs = Node:new(value, nil, nil)
            else
                self.lhs:Insert(value)
            end
            return
        else
            if self.rhs == nil then
                self.rhs = Node:new(value, nil, nil)
            else
                self.rhs:Insert(value)
            end
            return
        end
    end
    
    function Node:InOrder(output)
        if self.lhs ~= nil then
           output = self.lhs:InOrder(output)
        end
        output = self:printSelf(output)
        if self.rhs ~= nil then
            output = self.rhs:InOrder(output)
        end
        return output
    end
    
    function Node:printSelf(output)
        for i=0,self.counter-1 do
            output = output .. tostring(self.value) .. " "
        end
        return output
    end
    
    function PrintArray(numbers)
        output = ""
        for i=0,#numbers do
            output = output .. tostring(numbers[i]) .. " "
        end    
        print(output)
    end
    
    function Treesort(numbers)
        rootNode = Node:new(numbers[0], nil, nil)
        for i=1,#numbers do
            rootNode:Insert(numbers[i])
        end
        print(rootNode:InOrder(""))
    end
    
    
    numbersCount = 10
    maxNumber = 9
    
    numbers = {}
    
    for i=0,numbersCount-1 do
        numbers[i] = math.random(0, maxNumber)
    end
    
    PrintArray(numbers)
    Treesort(numbers)

    Важный нюанс что для чисел которые равны вершине придумано множество интересных механизмов подцепления к ноде, я же просто добавил счетчик к классу вершины, при распечатке числа возвращаются по счетчику.

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/treesort

    Источники

    TreeSort Algorithm Explained and Implemented with Examples in Java | Sorting Algorithms | Geekific – YouTube

    Tree sort – YouTube

    Convert Sorted Array to Binary Search Tree (LeetCode 108. Algorithm Explained) – YouTube

    Sorting algorithms/Tree sort on a linked list – Rosetta Code

    Tree Sort – GeeksforGeeks

    Tree sort – Wikipedia

    How to handle duplicates in Binary Search Tree? – GeeksforGeeks

    Tree Sort | GeeksforGeeks – YouTube

    バケットソート

    バケット並べ替え – バケットごとに並べ替えます。このアルゴリズムはカウンティングソートに似ていますが、数値が「バケット」範囲に収集され、その後、他の十分に生産性の高いソートアルゴリズムを使用してバケットがソートされ、最後のステップで「バケット」の範囲が展開される点が異なります。 1 ずつ増やすと、ソートされたリストが得られます。

    アルゴリズムの時間計算量は O(nk) です。このアルゴリズムは、一様分布の法則に従うデータに対して線形時間で機能します。簡単に言うと、要素は「スパイク」のない特定の範囲内にある必要があります (たとえば、0.0 から 1.0 までの数値)。そのような数字の中に 4 または 999 がある場合、中庭法によれば、そのような列は「偶数」とみなされなくなります。

    Julia での実装例:

        buckets = Vector{Vector{Int}}()
        
        for i in 0:bucketsCount - 1
            bucket = Vector{Int}()
            push!(buckets, bucket)
        end
    
        maxNumber = maximum(numbers)
    
        for i in 0:length(numbers) - 1
            bucketIndex = 1 + Int(floor(bucketsCount * numbers[1 + i] / (maxNumber + 1)))
            push!(buckets[bucketIndex], numbers[1 + i])
        end
    
        for i in 0:length(buckets) - 1
            bucketIndex = 1 + i
            buckets[bucketIndex] = sort(buckets[bucketIndex])
        end
    
        flat = [(buckets...)...]
        print(flat, "\n")
    
    end
    
    numbersCount = 10
    maxNumber = 10
    numbers = rand(1:maxNumber, numbersCount)
    print(numbers,"\n")
    bucketsCount = 10
    bucketSort(numbers, bucketsCount)

    На производительность алгоритма также влияет число ведер, для большего количества чисел лучше взять большее число ведер (Algorithms in a nutshell by George T. Heineman)

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/bucketSort

    Источники

    https://www.youtube.com/watch?v=VuXbEb5ywrU
    https://www.youtube.com/watch?v=ELrhrrCjDOA
    https://medium.com/karuna-sehgal/an-introduction-to-bucket-sort-62aa5325d124
    https://www.geeksforgeeks.org/bucket-sort-2/
    https://ru.wikipedia.org/wiki/%D0%91%D0%BB%D0%BE%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
    https://www.youtube.com/watch?v=LPrF9yEKTks
    https://en.wikipedia.org/wiki/Bucket_sort
    https://julialang.org/
    https://www.oreilly.com/library/view/algorithms-in-a/9780596516246/ch04s08.html

    基数ソート

    基数ソート – 基数ソート。このアルゴリズムは、要素の比較がないという点でカウント ソートに似ています。代わりに、要素が *文字ごと* に *バケット* (バケット) にグループ化され、バケットは現在の数値文字のインデックスによって選択されます。時間計算量 – O(nd)。

    次のように動作します:

    • 入力は数字 6、12、44、9 になります
    • リスト (0 ~ 9) のバケットを 10 個作成し、そこに少しずつ数字を追加/並べ替えます。

    次へ:

    <オル>

  • 数値内の最大文字数までのカウンター i でループを開始します
  • インデックス i を右から左に指定すると、各数値に対して 1 つの記号が得られ、記号がない場合は 0 であると見なされます。
  • 記号を数値に変換する
  • インデックス番号でバケットを選択し、そこに整数を入力します
  • 数値の検索が終了したら、すべてのバケットを数値のリストに変換し直します
  • ランク順に並べ替えられた数値を取得する
  • すべての数字がなくなるまで繰り返します
  • Scala での基数ソートの例:

    
    import scala.util.Random.nextInt
    
    
    
    object RadixSort {
    
        def main(args: Array[String]) = {
    
            var maxNumber = 200
    
            var numbersCount = 30
    
            var maxLength = maxNumber.toString.length() - 1
    
    
    
            var referenceNumbers = LazyList.continually(nextInt(maxNumber + 1)).take(numbersCount).toList
    
            var numbers = referenceNumbers
    
            
    
            var buckets = List.fill(10)(ListBuffer[Int]())
    
    
    
            for( i <- 0 to maxLength) { numbers.foreach( number => {
    
                        var numberString = number.toString
    
                        if (numberString.length() > i) {
    
                            var index = numberString.length() - i - 1
    
                            var character = numberString.charAt(index).toString
    
                            var characterInteger = character.toInt  
    
                            buckets.apply(characterInteger) += number
    
                        }
    
                        else {
    
                            buckets.apply(0) += number
    
                        }
    
                    }
    
                )
    
                numbers = buckets.flatten
    
                buckets.foreach(x => x.clear())
    
            }
    
            println(referenceNumbers)
    
            println(numbers)
    
            println(s"Validation result: ${numbers == referenceNumbers.sorted}")
    
        }
    
    }
    
    

    このアルゴリズムには、GPU などで並列実行するためのバージョンもあります。ちょっとした並べ替えオプションもあります。これは非常に興味深く、本当に息をのむようなものに違いありません。

    リンク

    https://gitlab .com/demensdeum/algorithms/-/blob/master/sortAlgorithms/radixSort/radixSort.scala

    ソース

    https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D0%B0%D0%B7%D1%80%D1%8F%D 0%B4%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0% BA%D0%B0
    https://www.geeksforgeeks.org/radix-sort/

    https://www.youtube.com/watch?v=toAlAJKojos

    https://github.com/gyatskov/radix-sort

    ヒープソート

    ヒープソート – ピラミッドソート。アルゴリズムの時間計算量 – O(n log n)、速いですよね?私はこれを、落ちてくる小石の選別と呼んでいます。これを説明する最も簡単な方法は視覚的に説明することだと思います。

    入力は、次のような数値のリストです。
    5、0、7、2、3、9、4

    左から右に、データ構造、つまりバイナリ ツリー、または私がそれを呼んでいる – が作成されます。ピラミッド。ピラミッド要素には最大 2 つの子要素を含めることができますが、最上位の要素は 1 つだけです。

    バイナリ ツリーを作成しましょう:
    ⠀⠀5
    ⠀0⠀7
    2 3 9 4

    ピラミッドを長い間見てみると、これらは配列からの単なる数値であり、次々に来て、各フロアの要素の数が 2 倍になっていることがわかります。

    次に、楽しい作業が始まります。小石を落とす方法 (heapify) を使用して、ピラミッドを下から上に並べ替えましょう。最終階(2 3 9 4)から並べ替えを開始することもできますが、意味がありません。以下に転落する可能性のある床はありません。

    したがって、最後から 2 番目のフロア (0 7) から要素をドロップし始めます。
    ⠀⠀5
    ⠀0⠀7
    2 3 9 4

    最初に該当する要素が右から選択されます。この場合は 7 です。次に、その下にあるものを調べます。その下には 9 と 4 があり、9 は 4 より大きく、9 はより大きいです。セブン! 7 を 9 に落とし、9 を持ち上げて 7 の位置に置きます。
    ⠀⠀5
    ⠀0⠀9
    2 3 7 4

    次に、7 には下に落ちるところがないことがわかり、左側の最後から 2 番目の階にある番号 0 に進みます。
    ⠀⠀5
    0⠀9
    2 3 7 4

    その下にあるものを見てみましょう – 2 と 3、2 は 3 より小さく、3 は 0 より大きいので、0 と 3 を交換します。
    ⠀⠀5
    ⠀3⠀9
    2 0 7 4

    フロアの端に到達したら、上のフロアに行き、可能であればそこにすべてを落としてください。
    結果はデー​​タ構造、つまりヒープ、つまり最大ヒープになります。一番上にあるのは最大の要素です。
    ⠀⠀9
    ⠀3⠀7
    2 0 5 4

    これを配列表現に戻すと、リストが得られます。
    [9、3、7、2、0、5、4]

    このことから、最初と最後の要素を交換することで、最終的な並べ替え位置の最初の数値が得られる、つまり 9 が並べ替えられたリストの最後にあるはずであると結論付けることができ、場所を入れ替えます。
    [4、3、7、2、0、5、9]

    二分木を見てみましょう:
    ⠀⠀4
    ⠀3⠀7
    2 0 5 9

    結果は、ツリーの下の部分がソートされた状況になります。必要なのは、4 を正しい位置にドロップし、アルゴリズムを繰り返すだけです。ただし、すでにソートされている数値、つまり 9 は考慮しません。
    ⠀⠀4
    ⠀3⠀7
    2 0 5 9

    ⠀⠀7
    ⠀3⠀4
    2 0 5 9

    ⠀⠀7
    ⠀3⠀5
    2 0 4 9

    4 を落とした後、9 – の次に大きな数字を上げたことが判明しました。 7. ソートされていない最後の数字 (4) と最大の数字 (7) を交換します
    ⠀⠀4
    ⠀3⠀5
    2 0 7 9

    正しい最終位置に 2 つの数値があることがわかりました。
    4、3、5、2、0、79

    次に、既に並べ替えられたアルゴリズムを無視して並べ替えアルゴリズムを繰り返します。最終的には ヒープ タイプ:
    ⠀⠀0
    ⠀2⠀3
    4 5 7 9

    またはリストとして:
    0、2、3、4、5、7、9

    実装

    アルゴリズムは通常、次の 3 つの機能に分割されます。

    <オル>

  • ヒープの作成
  • ふるい分けアルゴリズム (heapify)
  • ソートされていない最後の要素と最初の要素を置き換えます
  • ヒープは、heapify 関数を使用してバイナリ ツリーの最後から 2 番目の行を右から左に配列の末尾まで走査することによって作成されます。次にサイクルでは、最初の数値の置換が行われ、その後、最初の要素が配置されるか、その位置に残ります。その結果、最大の要素が 1 位に配置され、参加者が 1 人減ってサイクルが繰り返されます。各パスの後、ソートされた数値がリストの最後に残ります。

    Ruby でのヒープソートの例:

    
    
    
    
    
    module Colors
    
    
    
        BLUE = "\033[94m"
    
    
    
        RED = "\033[31m"
    
    
    
        STOP = "\033[0m"
    
    
    
    end
    
    
    
    
    
    
    
    def heapsort(rawNumbers)
    
    
    
        numbers = rawNumbers.dup
    
    
    
    
    
    
    
        def swap(numbers, from, to)
    
    
    
            temp = numbers[from]
    
    
    
            numbers[from] = numbers[to]
    
    
    
            numbers[to] = temp
    
    
    
        end
    
    
    
    
    
    
    
        def heapify(numbers)
    
    
    
            count = numbers.length()
    
    
    
            lastParentNode = (count - 2) / 2
    
    
    
    
    
    
    
            for start in lastParentNode.downto(0)
    
    
    
                siftDown(numbers, start, count - 1)
    
    
    
                start -= 1 
    
    
    
            end
    
    
    
    
    
    
    
            if DEMO
    
    
    
                puts "--- heapify ends ---"
    
    
    
            end
    
    
    
        end
    
    
    
    
    
    
    
        def siftDown(numbers, start, rightBound)      
    
    
    
            cursor = start
    
    
    
            printBinaryHeap(numbers, cursor, rightBound)
    
    
    
    
    
    
    
            def calculateLhsChildIndex(cursor)
    
    
    
                return cursor * 2 + 1
    
    
    
            end
    
    
    
    
    
    
    
            def calculateRhsChildIndex(cursor)
    
    
    
                return cursor * 2 + 2
    
    
    
            end            
    
    
    
    
    
    
    
            while calculateLhsChildIndex(cursor) <= rightBound
    
    
    
                lhsChildIndex = calculateLhsChildIndex(cursor)
    
    
    
                rhsChildIndex = calculateRhsChildIndex(cursor)
    
    
    
    
    
    
    
                lhsNumber = numbers[lhsChildIndex]
    
    
    
                biggerChildIndex = lhsChildIndex
    
    
    
    
    
    
    
                if rhsChildIndex <= rightBound
    
    
    
                    rhsNumber = numbers[rhsChildIndex]
    
    
    
                    if lhsNumber < rhsNumber
    
    
    
                        biggerChildIndex = rhsChildIndex
    
    
    
                    end
    
    
    
                end
    
    
    
    
    
    
    
                if numbers[cursor] < numbers[biggerChildIndex]
    
    
    
                    swap(numbers, cursor, biggerChildIndex)
    
    
    
                    cursor = biggerChildIndex
    
    
    
                else
    
    
    
                    break
    
    
    
                end
    
    
    
                printBinaryHeap(numbers, cursor, rightBound)
    
    
    
            end
    
    
    
            printBinaryHeap(numbers, cursor, rightBound)
    
    
    
        end
    
    
    
    
    
    
    
        def printBinaryHeap(numbers, nodeIndex = -1, rightBound = -1)
    
    
    
            if DEMO == false
    
    
    
                return
    
    
    
            end
    
    
    
            perLineWidth = (numbers.length() * 4).to_i
    
    
    
            linesCount = Math.log2(numbers.length()).ceil()
    
    
    
            xPrinterCount = 1
    
    
    
            cursor = 0
    
    
    
            spacing = 3
    
    
    
            for y in (0..linesCount)
    
    
    
                line = perLineWidth.times.map { " " }
    
    
    
                spacing = spacing == 3 ? 4 : 3
    
    
    
                printIndex = (perLineWidth / 2) - (spacing * xPrinterCount) / 2
    
    
    
                for x in (0..xPrinterCount - 1)
    
    
    
                    if cursor >= numbers.length
    
    
    
                        break
    
    
    
                    end
    
    
    
                    if nodeIndex != -1 && cursor == nodeIndex
    
    
    
                        line[printIndex] = "%s%s%s" % [Colors::RED, numbers[cursor].to_s, Colors::STOP]
    
    
    
                    elsif rightBound != -1 && cursor > rightBound
    
    
    
                        line[printIndex] = "%s%s%s" % [Colors::BLUE, numbers[cursor].to_s, Colors::STOP]
    
    
    
                    else
    
    
    
                        line[printIndex] = numbers[cursor].to_s
    
    
    
                    end
    
    
    
                    cursor += 1
    
    
    
                    printIndex += spacing
    
    
    
                end
    
    
    
                print line.join()
    
    
    
                xPrinterCount *= 2           
    
    
    
                print "\n"            
    
    
    
            end
    
    
    
        end
    
    
    
    
    
    
    
        heapify(numbers)
    
    
    
        rightBound = numbers.length() - 1
    
    
    
    
    
    
    
        while rightBound > 0
    
    
    
            swap(numbers, 0, rightBound)   
    
    
    
            rightBound -= 1
    
    
    
            siftDown(numbers, 0, rightBound)     
    
    
    
        end
    
    
    
    
    
    
    
        return numbers
    
    
    
    end
    
    
    
    
    
    
    
    numbersCount = 14
    
    
    
    maximalNumber = 10
    
    
    
    numbers = numbersCount.times.map { Random.rand(maximalNumber) }
    
    
    
    print numbers
    
    
    
    print "\n---\n"
    
    
    
    
    
    
    
    start = Time.now
    
    
    
    sortedNumbers = heapsort(numbers)
    
    
    
    finish = Time.now
    
    
    
    heapSortTime = start - finish
    
    
    
    
    
    
    
    start = Time.now
    
    
    
    referenceSortedNumbers = numbers.sort()
    
    
    
    finish = Time.now
    
    
    
    referenceSortTime = start - finish
    
    
    
    
    
    
    
    print "Reference sort: "
    
    
    
    print referenceSortedNumbers
    
    
    
    print "\n"
    
    
    
    print "Reference sort time: %f\n" % referenceSortTime
    
    
    
    print "Heap sort:      "
    
    
    
    print sortedNumbers
    
    
    
    print "\n"
    
    
    
    if DEMO == false
    
    
    
        print "Heap sort time:      %f\n" % heapSortTime
    
    
    
    else
    
    
    
        print "Disable DEMO for performance measure\n"
    
    
    
    end
    
    
    
    
    
    
    
    if sortedNumbers != referenceSortedNumbers
    
    
    
        puts "Validation failed"
    
    
    
        exit 1
    
    
    
    else
    
    
    
        puts "Validation success"
    
    
    
        exit 0
    
    
    
    end
    
    
    
    

    このアルゴリズムは視覚化しないと理解しにくいため、最初にバイナリ ツリーの現在のビューを出力する関数を作成することをお勧めします。

    リンク

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/heapsort/heapsort.rb

    ソース

    http://rosettacode.org/wiki/Sorting_algorithms/Heapsort
    https://www.youtube.com/watch?v=LbB357_RwlY

    https://habr.com/ru/company/ otus/blog/460087/

    https://ru.wikipedia.org/wiki/Pyramid_sort

    https://neerc.ifmo.ru/wiki /index.php?title=ヒープソート

    https://wiki5.ru/wiki/Heapsort

    https://wiki.c2.com/?HeapSort

    https://ru.wikipedia.org/wiki/Tree (データ構造)

    https://ru.wikipedia.org/wiki/Heap (データ構造)

    https://www.youtube.com/watch?v=2DmK_H7IdTo

    https://www.youtube.com/watch?v=kU4KBD4NFtw

    https://www.youtube.com/watch?v=DU1uG5310x0

    https://www.youtube.com/watch?v =BzQGPA_v-vc

    https://www.geeksforgeeks.org/バイナリヒープの配列表現/

    https://habr.com/ru/post/112222/

    https://www.cs.usfca. edu/~galles/visualization/BST.html

    https://www.youtube.com/watch?v=EQzqHWtsKq4

    https://medium.com/@dimko1/%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC% D1 %8B-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8-heapsort-796ba965018b

    https://ru.wikibrief.org/wiki/Heapsort

    https://www.youtube.com/watch?v=GUUpmrTnNbw

    Bumblebee All Troubles

    Recently, it turned out that users of modern Nvidia GPUs under Arch Linux do not need to use the bumblebee package at all, for example, for me it did not detect an external monitor when connected. I recommend removing the bumblebee package and all related packages, and installing prime using the instructions on the Arch Wiki.
    Next, to launch all games on Steam and 3D applications, add prime-run, for Steam this is done like this prime-run %command% in additional launch options.
    To check the correctness, you can use glxgears, prime-run glxgears.
    https://bbs.archlinux.org/viewtopic.php? pid=2048195#p2048195

    クイックソート

    クイックソートは分割統治型の並べ替えアルゴリズムです。再帰的に、部分ごとに数値の配列を解析し、選択した参照要素から小さい順に大きい順に数値を配置し、参照要素自体をそれらの間のカットオフに挿入します。何度か再帰的に繰り返すと、ソートされたリストが完成します。時間計算量 O(n2)。

    スキーム:

    <オル>

  • まず、外側から要素のリスト、つまり並べ替え境界を取得します。最初のステップでは、並べ替えの境界は最初から最後までになります。
  • 開始境界と終了境界が交差していないことを確認し、交差している場合は終了します。
  • リストから要素を選択し、それをピボットと呼びます
  • 邪魔にならないように、最後のインデックスまでに右に移動してください
  • ゼロに等しい *小さい数値* のカウンターを作成します
  • リストを左から右に、参照要素が配置されている最後のインデックスまでループします
  • 各要素を参照要素と比較します
  • それが参照値よりも小さい場合は、より小さい数値のカウンターのインデックスに従ってそれを交換します。より小さい数値のカウンタをインクリメントします。
  • ループがサポート要素に到達すると、停止し、より小さい数値のカウンターに従ってサポート要素を要素と交換します。
  • リストの左側の小さい部分と、リストの右側の大きい部分に対してアルゴリズムを別々に実行します。
  • その結果、ポイント 2 のチェックインにより、すべての再帰的反復が停止し始める
  • ソートされたリストを取得する
  • クイックソートは、モスクワ州立大学の科学者チャールズ アンソニー リチャード ホアによって発明されました。彼はロシア語を学び、コルモゴロフ学校でコンピューター翻訳と確率論を学びました。 1960 年、政治危機のため、彼はソ連を去りました。

    Rust での実装例:

    
    use rand::Rng;
    
    fn swap(numbers: &mut [i64], from: usize, to: usize) {
        let temp = numbers[from];
        numbers[from] = numbers[to];
        numbers[to] = temp;
    }
    
    fn quicksort(numbers: &mut [i64], left: usize, right: usize) {
        if left >= right {
            return
        }
        let length = right - left;
        if length <= 1 {
            return
        }
        let pivot_index = left + (length / 2);
        let pivot = numbers[pivot_index];
    
        let last_index = right - 1;
        swap(numbers, pivot_index, last_index);
    
        let mut less_insert_index = left;
    
        for i in left..last_index {
            if numbers[i] < pivot {
                swap(numbers, i, less_insert_index);
                less_insert_index += 1;
            }
        }
        swap(numbers, last_index, less_insert_index);
        quicksort(numbers, left, less_insert_index);
        quicksort(numbers, less_insert_index + 1, right);
    }
    
    fn main() {
        let mut numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let mut reference_numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    
        let mut rng = rand::thread_rng();
        for i in 0..numbers.len() {
            numbers[i] = rng.gen_range(-10..10);
            reference_numbers[i] = numbers[i];
        }
    
        reference_numbers.sort();
    
      println!("Numbers           {:?}", numbers);
      let length = numbers.len();
      quicksort(&mut numbers, 0, length);
      println!("Numbers           {:?}", numbers);
      println!("Reference numbers {:?}", reference_numbers);
    
      if numbers != reference_numbers {
        println!("Validation failed");
        std::process::exit(1);
      }
      else {
        println!("Validation success!");
        std::process::exit(0);
      }
    }
    

    何も明確でない場合は、サンディエゴ大学の Rob Edwards によるビデオを見ることをお勧めします https://www.youtube.com/watch?v=ZHVk2blR45Q アルゴリズムの本質と実装をステップごとに最も簡単に示しています。

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/quickSort

    ソース

    https://www.youtube.com/watch?v =4s-aG6yGGLU
    https://www.youtube.com/watch?v=ywWBy6J5gz8
    https://www.youtube.com/watch?v=Hoixgm4-P4M
    https://ru.wikipedia.org/wiki/Быстрая_сортировка
    https://www.youtube.com/watch?v=Hoixgm4-P4M
    https://www.youtube.com/watch?v=XE4VP_8Y0BU
    https://www.youtube.com/watch?v=MZaf_9IZCrc
    https://www.youtube.com/watch?v=ZHVk2blR45Q
    http://rosettacode.org/wiki/Sorting_algorithms/Quicksort
    https://www.youtube.com/watch?v=4s-aG6yGGLU
    https://www.youtube.com/watch?v=dQw4w9WgXcQ
    https://www.youtube.com/watch?v=maibrCbZWKw
    https://www.geeksforgeeks.org/quick-sort/
    https://www.youtube.com/watch?v=uXBnyYuwPe8

    バイナリ挿入ソート

    二分挿入ソートは、二分検索を使用して挿入位置を決定する挿入ソートの変形です。アルゴリズムの時間計算量は O(n2) です

    アルゴリズムは次のように機能します:

    <オル>

  • ループは 0 からリストの最後まで始まります
  • ループ内で、並べ替えのために数値が選択され、その数値は別の変数に保存されます
  • 二分検索では、左側の数値に対してこの数値を挿入するためのインデックスが検索されます
  • インデックスが見つかると、挿入インデックスから開始して、左側の数字が 1 つ右にシフトされます。この過程で、並べ替える必要がある数値は消去されます。
  • 以前に保存した番号が挿入インデックスに挿入されます
  • ループの最後で、リスト全体が並べ替えられます
  • バイナリ検索中に、数値が見つからず、インデックスが返されない可能性があります。二分検索の特殊性により、検索された数値に最も近い数値が検索され、インデックスを返すには、そのインデックスを求めた数値と比較する必要があります。求めた数値が小さい場合、求めた数値は次の位置にある必要があります。インデックスが左側にあり、それ以上の場合は右側にあります。

    Go コード:

    
    import (
    	"fmt"
    	"math/rand"
    	"time"
    )
    
    const numbersCount = 20
    const maximalNumber = 100
    
    func binarySearch(numbers []int, item int, low int, high int) int {
    	for high > low {
    		center := (low + high) / 2
    		if numbers[center] < item { low = center + 1 } else if numbers[center] > item {
    			high = center - 1
    		} else {
    			return center
    		}
    	}
    
    	if numbers[low] < item {
    		return low + 1
    	} else {
    		return low
    	}
    }
    
    func main() {
    	rand.Seed(time.Now().Unix())
    	var numbers [numbersCount]int
    	for i := 0; i < numbersCount; i++ {
    		numbers[i] = rand.Intn(maximalNumber)
    	}
    	fmt.Println(numbers)
    
    	for i := 1; i < len(numbers); i++ { searchAreaLastIndex := i - 1 insertNumber := numbers[i] insertIndex := binarySearch(numbers[:], insertNumber, 0, searchAreaLastIndex) for x := searchAreaLastIndex; x >= insertIndex; x-- {
    			numbers[x+1] = numbers[x]
    		}
    		numbers[insertIndex] = insertNumber
    	}
    	fmt.Println(numbers)
    }
    

    リンク

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/binaryInsertionSort/binaryInsertionSort.go

    ソース

    https://www.geeksforgeeks.org/binary-insertion-並べ替え/
    https://www.youtube.com/watch?v=-OVB5pOZJug

    シェルソート

    シェル ソート – 数値の配列を事前に組み合わせた挿入ソートの変形です。

    挿入ソートがどのように機能するかを覚えておく必要があります。

    1.ループは0からループの終わりまで開始されるため、配列は2つの部分に分割されます
    2. 左側の部分では、2 番目のループが開始され、要素を右から左に比較します。左側の小さい要素が見つかるまで、右側の小さい要素が削除されます。
    3. 両方のループの最後に、ソートされたリストが得られます。

    昔々、コンピューター科学者のドナルド シェルは、挿入ソート アルゴリズムを改善する方法を考えていました。彼はまた、最初に配列を 2 サイクルで処理するが、一定の距離を置き、通常の挿入ソート アルゴリズムに変わるまで「コーム」を徐々に減らしていくというアイデアも思いつきました。すべては非常に単純で、落とし穴はありません。上記の 2 つのサイクルに別のサイクルを追加し、「コーム」のサイズを徐々に小さくします。必要なのは、比較するときに配列を超えないよう距離を確認することだけです。

    本当に興味深いトピックは、最初のループの各反復で比較長を変更するためのシーケンスを選択することです。アルゴリズムのパフォーマンスがアルゴリズムに依存するという理由から、これは興味深いものです。

    既知のオプションと時間計算量の表は、https: //en .wikipedia.org/wiki/Shellsort#Gap_sequences

    理想的な距離の計算にはさまざまな人々が関与しており、このトピックは彼らにとって非常に興味深いものだったようです。 Ruby を実行して、最速の sort() アルゴリズムを呼び出すことはできないでしょうか?

    一般に、これらの奇妙な人々は、シェル アルゴリズムの「櫛」の距離/ギャップの計算をテーマに論文を書きました。私は彼らの研究結果を単純に使用し、Hibbard、Knuth-Pratt、Chiura、Sedgwick の 5 種類のシーケンスをチェックしました。

    import time
    import random
    from functools import reduce
    import math
    
    DEMO_MODE = False
    
    if input("Demo Mode Y/N? ").upper() == "Y":
        DEMO_MODE = True
    
    class Colors:
        BLUE = '\033[94m'
        RED = '\033[31m'
        END = '\033[0m'
    
    def swap(list, lhs, rhs):
        list[lhs], list[rhs] = list[rhs], list[lhs]
        return list
    
    def colorPrintoutStep(numbers: List[int], lhs: int, rhs: int):
        for index, number in enumerate(numbers):
            if index == lhs:
                print(f"{Colors.BLUE}", end = "")
            elif index == rhs:
                print(f"{Colors.RED}", end = "")
            print(f"{number},", end = "")
            if index == lhs or index == rhs:
                print(f"{Colors.END}", end = "")
            if index == lhs or index == rhs:
                print(f"{Colors.END}", end = "")
        print("\n")
        input(">")
    
    def ShellSortLoop(numbers: List[int], distanceSequence: List[int]):
        distanceSequenceIterator = reversed(distanceSequence)
        while distance:= next(distanceSequenceIterator, None):
            for sortArea in range(0, len(numbers)):
                for rhs in reversed(range(distance, sortArea + 1)):
                    lhs = rhs - distance
                    if DEMO_MODE:
                        print(f"Distance: {distance}")
                        colorPrintoutStep(numbers, lhs, rhs)
                    if numbers[lhs] > numbers[rhs]:
                        swap(numbers, lhs, rhs)
                    else:
                        break
    
    def ShellSort(numbers: List[int]):
        global ShellSequence
        ShellSortLoop(numbers, ShellSequence)
    
    def HibbardSort(numbers: List[int]):
        global HibbardSequence
        ShellSortLoop(numbers, HibbardSequence)
    
    def ShellPlusKnuttPrattSort(numbers: List[int]):
        global KnuttPrattSequence
        ShellSortLoop(numbers, KnuttPrattSequence)
    
    def ShellPlusCiuraSort(numbers: List[int]):
        global CiuraSequence
        ShellSortLoop(numbers, CiuraSequence)
    
    def ShellPlusSedgewickSort(numbers: List[int]):
        global SedgewickSequence
        ShellSortLoop(numbers, SedgewickSequence)
    
    def insertionSort(numbers: List[int]):
        global insertionSortDistanceSequence
        ShellSortLoop(numbers, insertionSortDistanceSequence)
    
    def defaultSort(numbers: List[int]):
        numbers.sort()
    
    def measureExecution(inputNumbers: List[int], algorithmName: str, algorithm):
        if DEMO_MODE:
            print(f"{algorithmName} started")
        numbers = inputNumbers.copy()
        startTime = time.perf_counter()
        algorithm(numbers)
        endTime = time.perf_counter()
        print(f"{algorithmName} performance: {endTime - startTime}")
    
    def sortedNumbersAsString(inputNumbers: List[int], algorithm) -> str:
        numbers = inputNumbers.copy()
        algorithm(numbers)
        return str(numbers)
    
    if DEMO_MODE:
        maximalNumber = 10
        numbersCount = 10
    else:
        maximalNumber = 10
        numbersCount = random.randint(10000, 20000)
    
    randomNumbers = [random.randrange(1, maximalNumber) for i in range(numbersCount)]
    
    ShellSequenceGenerator = lambda n: reduce(lambda x, _: x + [int(x[-1]/2)], range(int(math.log(numbersCount, 2))), [int(numbersCount / 2)])
    ShellSequence = ShellSequenceGenerator(randomNumbers)
    ShellSequence.reverse()
    ShellSequence.pop()
    
    HibbardSequence = [
        0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095,
        8191, 16383, 32767, 65535, 131071, 262143, 524287, 1048575,
        2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727,
        268435455, 536870911, 1073741823, 2147483647, 4294967295, 8589934591
    ]
    
    KnuttPrattSequence = [
        1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 
        797161, 2391484, 7174453, 21523360, 64570081, 193710244, 581130733, 
        1743392200, 5230176601, 15690529804, 47071589413
    ]
    
    CiuraSequence = [
                1, 4, 10, 23, 57, 132, 301, 701, 1750, 4376, 
                10941, 27353, 68383, 170958, 427396, 1068491, 
                2671228, 6678071, 16695178, 41737946, 104344866, 
                260862166, 652155416, 1630388541
    ]
    
    SedgewickSequence = [
                1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905,
                8929, 16001, 36289, 64769, 146305, 260609, 587521, 
                1045505, 2354689, 4188161, 9427969, 16764929, 37730305, 
                67084289, 150958081, 268386305, 603906049, 1073643521, 
                2415771649, 4294770689, 9663381505, 17179475969
    ]
    
    insertionSortDistanceSequence = [1]
    
    algorithms = {
        "Default Python Sort": defaultSort,
        "Shell Sort": ShellSort,
        "Shell + Hibbard" : HibbardSort,
        "Shell + Prat, Knutt": ShellPlusKnuttPrattSort,
        "Shell + Ciura Sort": ShellPlusCiuraSort,
        "Shell + Sedgewick Sort": ShellPlusSedgewickSort,
        "Insertion Sort": insertionSort
    }
    
    for name, algorithm in algorithms.items():
        measureExecution(randomNumbers, name, algorithm)
    
    reference = sortedNumbersAsString(randomNumbers, defaultSort)
    
    for name, algorithm in algorithms.items():
        if sortedNumbersAsString(randomNumbers, algorithm) != reference:
            print("Sorting validation failed")
            exit(1)
    
    print("Sorting validation success")
    exit(0)
    

    私の実装では、ランダムな数値セットの場合、最も速いギャップはセジウィックとヒバードです。

    マイピー

    Python 3 – の静的型付けアナライザーについても触れておきたいと思います。マイピー。動的型付けを使用する言語に固有の問題に対処するのに役立ちます。つまり、不必要な場所に何かが貼り付けられる可能性を排除します。

    経験豊富なプログラマーが言うように、「専門家のチームがいる場合、静的型付けは必要ありません」。いつか私たち全員が専門家になり、完全に統一してマシンを理解しながらコードを書くようになりますが、今のところは同様のユーティリティを使用できます。静的型付け言語。

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/shellSort
    http://mypy-lang.org/

    ソース

    https://dl.acm.org/doi/10.1145/368370.368387
    https://en.wikipedia.org/wiki/Shellsort
    http://rosettacode.org/wiki/Sorting_algorithms/Shell_sort
    https://ru.wikipedia.org/wiki/Сортировка_Шелла
    https://neerc.ifmo.ru/wiki/index.php?title=Сортировка_Шелла
    https://twitter.com/gvanrossum/status/700741601966985216

    二重選択ソート

    Double Selection Sort – 選択ソートのサブタイプで、速度が 2 倍になるようです。バニラのアルゴリズムは、数値のリストを 2 回ループし、最小の数値を見つけて、上のレベルのループが指す現在の数値と位置を交換します。二重選択ソートでは、最小値と最大値を検索し、– より上のレベルでループが指す 2 桁を置き換えます。左右に 2 つの数字。この乱交全体は、置換される数字のカーソルがリストの中央に見つかると終了します。その結果、ソートされた数字が視覚的中心の左右に取得されます。
    アルゴリズムの時間計算量は、選択ソートと同様です。 O(n2) ですが、おそらく 30 の加速度があると思われます%。

    境界線の状態

    この段階ですでに、衝突の瞬間を想像することができます。たとえば、左カーソルの番号 (最小値) がリスト内の最大値を指しているとき、最小値が再配置され、再配置が行われます。最大数がすぐに壊れてしまいます。したがって、アルゴリズムのすべての実装には、そのようなケースのチェックとインデックスの正しいものへの置き換えが含まれます。私の実装では、1 回のチェックで十分でした。

      maximalNumberIndex = minimalNumberIndex;
    }

    Реализация на Cito

    Cito – язык либ, язык транслятор. На нем можно писать для C, C++, C#, Java, JavaScript, Python, Swift, TypeScript, OpenCL C, при этом совершенно ничего не зная про эти языки. Исходный код на языке Cito транслируется в исходный код на поддерживаемых языках, далее можно использовать как библиотеку, либо напрямую, исправив сгенеренный код руками. Эдакий Write once – translate to anything.
    Double Selection Sort на cito:

    {
        public static int[] sort(int[]# numbers, int length)
        {
            int[]# sortedNumbers = new int[length];
            for (int i = 0; i < length; i++) {
                sortedNumbers[i] = numbers[i];
            }
            for (int leftCursor = 0; leftCursor < length / 2; leftCursor++) {
                int minimalNumberIndex = leftCursor;
                int minimalNumber = sortedNumbers[leftCursor];
    
                int rightCursor = length - (leftCursor + 1);
                int maximalNumberIndex = rightCursor;
                int maximalNumber = sortedNumbers[maximalNumberIndex];
    
                for (int cursor = leftCursor; cursor <= rightCursor; cursor++) { int cursorNumber = sortedNumbers[cursor]; if (minimalNumber > cursorNumber) {
                        minimalNumber = cursorNumber;
                        minimalNumberIndex = cursor;
                    }
                    if (maximalNumber < cursorNumber) {
                        maximalNumber = cursorNumber;
                        maximalNumberIndex = cursor;
                    }
                }
    
                if (leftCursor == maximalNumberIndex) {
                    maximalNumberIndex = minimalNumberIndex;
                }
    
                int fromNumber = sortedNumbers[leftCursor];
                int toNumber = sortedNumbers[minimalNumberIndex];
                sortedNumbers[minimalNumberIndex] = fromNumber;
                sortedNumbers[leftCursor] = toNumber;
    
                fromNumber = sortedNumbers[rightCursor];
                toNumber = sortedNumbers[maximalNumberIndex];
                sortedNumbers[maximalNumberIndex] = fromNumber;
                sortedNumbers[rightCursor] = toNumber;
            }
            return sortedNumbers;
        }
    } 
    

    リンク

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/doubleSelectionSort
    https://github.com/pfusik/cito

    ソース

    https://www.researchgate.net/publication/330084245_改良_Double_Selection_Sort_using_Algorithm
    http://algolab.valemak.com/selection-double
    https://www.geeksforgeeks.org/sorting-algorithm-slightly-improves-selection-sort/

    カクテルシェーカーの並べ替え

    カクテル シェーカー ソート –双方向バブルソーティングの一種であるシェーカーソーティング。
    アルゴリズムは次のように機能します。

    <オル>

  • ループ内での最初の検索方向が選択されます (通常は左から右)
  • ループの次に、数値がペアでチェックされます
  • 次の要素の方が大きい場合、それらは交換されます
  • 終了すると、検索プロセスが方向を逆にして再び開始されます
  • 検索は順列がなくなるまで繰り返されます
  • アルゴリズムの時間計算量はバブル – に似ています。 O(n2)

    PHP での実装例:

    <?php
    
    function cocktailShakeSort($numbers)
    {
        echo implode(",", $numbers),"\n";
        $direction = false;
        $sorted = false;
        do {
            $direction = !$direction;        
            $firstIndex = $direction == true ? 0 : count($numbers) - 1;
            $lastIndex = $direction == true ? count($numbers) - 1 : 0;
            
            $sorted = true;
            for (
                $i = $firstIndex;
                $direction == true ? $i < $lastIndex : $i > $lastIndex;
                $direction == true ? $i++ : $i--
            ) {
                $lhsIndex = $direction ? $i : $i - 1;
                $rhsIndex = $direction ? $i + 1 : $i;
    
                $lhs = $numbers[$lhsIndex];
                $rhs = $numbers[$rhsIndex];
    
                if ($lhs > $rhs) {
                    $numbers[$lhsIndex] = $rhs;
                    $numbers[$rhsIndex] = $lhs;
                    $sorted = false;
                }
            }
        } while ($sorted == false);
    
        echo implode(",", $numbers);
    }
    
    $numbers = [2, 1, 4, 3, 69, 35, 55, 7, 7, 2, 6, 203, 9];
    cocktailShakeSort($numbers);
    
    ?>

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/blob/master/sortAlgorithms/cocktailShakerSort/cocktailShakerSort.php

    Источники

    https://www.youtube.com/watch?v=njClLBoEbfI
    https://www.geeksforgeeks.org/cocktail-sort/
    https://rosettacode.org/wiki/Sorting_algorithms/Cocktail_sort

    ファットボーイサイズ –フォルダーとファイルのサイズを表示するユーティリティ

    ファットボーイサイズ –端末内のフォルダーやファイルのサイズを表示するユーティリティ
    です。Python 3 をサポートするあらゆるシステムで動作します。

    実行: python3 fbs.py
    出力モード 1: python3 fbs.py -v
    出力モード 2: python3 fbs.py --version

    ターミナルで現在開いているパスに対してのみ機能します。

    結果の例:
    python3 ~/Sources/my/fatboysize/fbs.py
    .local -> 145.GB
    ダウンロード -> 103.GB
    .キャッシュ -> 37.0GB
    .android -> 11.6GB
    ソース -> 8.63GB

    ご覧のとおり、ダウンロード フォルダーは非常に小さいですが、大きい

    です。

    リンク

    https://gitlab.com/demensdeum/fatboysize/p>