NB:ハブがポストをカットするという事実により、あまり価値のないコードをペーストバインに配置する必要がありました。 テキスト内のリンクに従ってください。
多くの人は、OCamlを限界的で難解なものとして分類しています。 おそらく彼らは正しいが、多くの人々は彼らに反対している。 彼との知り合いは6ヶ月前に始まりました。そのとき、私はもう一度何か新しいことを学びたいと思ったので、少なくとも1つの関数型言語を習得すべきだと思いました。 多くの言語の中からObjective Camlを選びました。 この言語は、人間のシンタックスとアイデアに魅了されました。人生にはすべての機能的な喜びがありますが、命令型のスタイルとOOPが必要な場合は、それを手に入れてください。 開発者は、ツールごとに異なる手段が必要であることを十分に認識していることがわかりました。 C ++およびPerlプログラマ向けのマニュアルを 3日間読んだところ、コードを読んでワードワードを書くことができました。 言語の勉強は本当の仕事ではないので、これで言語との知り合いは終わりました-それは愚かです。
数週間前に1つの興味深いタスクが発生したとき、私はOCamlに戻りました。 私はそれを見て、原理的にはPHPで完全に解決できること、または好きなようにC ++のPHP拡張機能を書くことができることに気付きましたが、関数型言語の方がはるかに適しているでしょう。 したがって、OCaml関数を呼び出したいPHPのメインプログラムがあり、そこから明らかな利益と、ばかとMichurinの1つのボトルの単純な喜びを受け取ります。 この記事では、原理を実証するための簡単なコードのみを提供します-トリッキーな最適化なしで、コードをはるかに高速にしますが、可読性を低下させます。 さらに、PHPとOCamlのラッパーを最大限に分離しようとしたため、データ変換はPHP←→OCamlによって直接行われず、PHP←→C←→OCamlのパスに沿って行われるため、さらに速度が低下します。 しかし、原則ははるかに理解しやすく、誰もが最適化に対処できることを願っています。
解決する問題を次のように定義します。PHPから構造体の配列を取得し、何らかの方法で値をフィルタリングするなどして、残りの配列を返す必要があります。
OCaml
始めましょう。 最初に、すべての作業を行うOCamlパーツを作成します。 ocamlpart.mlファイルで、使用するデータ型を定義します。 いくつかの化学グループにしましょう:
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
type group_position = UndefPos | LeftPos | RightPos ;; type cycle_type = UndefCycle | NoneCycle | AliphaticCycle | AromaticCycle | HeteroCycle ;; type group_type = OrgGroup | InorgGroup | NeighbourGroup of int ;; type group = { name : string ; position : group_position ; cycle : cycle_type ; grouptype : group_type ; link : int ; } ;;
C( group )構造の類似体、enum( group_position 、 cycle_type )の類似体、および列挙型または任意の数( group_type )のいずれかであるハイブリッド型があることに注意してください。 次に、フィルターする関数を追加します。
- let filter_org = List 。 フィルタ ( fun r- > if r。grouptype = OrgGroup then true else false )
この関数は、grouptypeフィールドの値でグループのリストをフィルターします-実際、これはPHPでarray_filter関数と呼ばれるものに類似しています。
最後に、Cで使用するフィルター関数の登録を追加します。
- let _ =
- コールバック 「filter_organic」 filter_orgを登録し ます。
- ;;
このブロックは、CからOCamlパーツを初期化するときに実行され、すばらしい名前のコールバックを提供します。
次に、このためのラッパーを作成する必要があります。
C / C ++
まず、共通のcpart.hファイルを準備します。このファイルには、OCamlラッパーとPHPラッパーの共通部分があります。
- #include <ベクトル>
- typedef enum _group_position {
- UndefPos = 0 、
- LeftPos、
- Rightpos
- } group_position ;
- typedef enum _cycle_type {
- UndefCycle = 0 、
- NoneCycle、
- AliphaticCycle、
- AromaticCycle、
- ヘテロサイクル
- } cycle_type ;
- typedef enum _group_pos {
- OrgGroup = -2 、
- InorgGroup = -1
- } group_pos ;
- typedef struct _group {
- char * name ;
- group_position位置;
- cycle_typeサイクル;
- int group_type ;
- intリンク;
- }グループ;
- void init_ocaml ( ) ;
- std :: vector < group > filter_org ( std :: vector < group > g ) ;
OCamlから型マッピングを決定し、OCamlを初期化するための2つの関数と、データ配列をフィルター処理する主な作業ラッパー関数を宣言しました。
実際、より多くの機能がありますが、PHPで将来表示する予定のある機能のみです。 次に、Ocamlのラッパーを含むccamlpart.ccファイルを作成しましょう。 最初の見出し:
- #include "cpart.h"
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- #include <caml / mlvalues.h>
- #include <caml / alloc.h>
- #include <caml / callback.h>
- #include <caml / fail.h>
- #include <caml / memory.h>
- #ifdef __cplusplus
- }
- #endif
- #include <string.h>
ベクターでC ++を使用し、プラスとしてコードをコンパイルする必要があるため、標準OCaml関数のexternを忘れないでください。 次に、OCamlとCの間の型変換のための補助関数を定義します( こちら )。
ここでは、ハイブリッドOCaml型group_typeを変換するための特別な関数が必要でした-その列挙値は素整数として格納されますが、NeighbourGroupはすでに単一フィールド構造です。
CAMLparamX、CAMLlocalX、CAMLreturn *の3つのマクロを見る価値があります。 それらの最初のものは、ガベージコレクターの正しい操作に使用され、入力として関数に渡されるすべてのOCaml変数を受け取ります。 2番目はローカルOCaml変数を宣言するために使用されます。 また、値を返すためだけでなく、ガベージコレクターの正しい操作のためにも、3番目のマクロが必要です。
最後に、PHPの現在の関数:
- void init_ocaml ( )
- {
- char * argv [ 1 ] ;
- argv [ 0 ] = NULL ;
- caml_main ( argv ) ;
- }
- std :: vector < group > filter_org ( std :: vector < group > g )
- {
- Camlparam0 ( ) ;
- 静的値* closure_f = NULL ;
- if ( closure_f == NULL )
- closure_f = caml_named_value ( "filter_organic" ) ;
- CAMLlocal3 ( cli、cons、cb_res ) ;
- cli = Val_emptylist ;
- for ( std :: vector < group > :: イテレーター i = g。begin ( ) ; i ! = g。end ( ) ; ++ i )
- {
- cons = caml_alloc ( 2、0 ) ;
- Store_field ( cons、 0 、camlgroup_of_group ( & ( * i ) ) ) ;
- Store_field ( cons、 1 、cli ) ;
- cli = cons ;
- }
- cb_res = caml_callback ( * closure_f、cli ) ;
- std :: vector < group > result ;
- while ( cb_res ! = Val_emptylist )
- {
- 結果。 push_back ( group_of_camlgroup ( Field ( cb_res、 0 ) ) ) ;
- cb_res =フィールド( cb_res、 1 ) ;
- }
- 結果を返す ;
- }
ベクターからOCamlシートを簡単な方法で作成します。文字通り、すべての規範に従って構築されます。最初のフィールドは要素で、2番目のフィールドはリストへのポインターです。 次に、このOCamlのリストをフィードし、結果をベクトルに戻します。
makefileを使用して、すべてを静的ライブラリにまとめます。
- ocamlpart 。 o : ocamlpart 。 ml
- ocamlopt - g-出力-obj $ ^ -o $ @
- ccamlpart 。 o : ccamlpart 。 cc
- g ++ - g - c - o $ @ -I "` ocamlc -where` " $ ^
- libchempart 。 a : ocamlpart 。 o ccamlpart 。 o
- ar rcs $ @ $ ^
- all : libchempart 。 a
- きれい:
- rm - f *。 o *。 *。 cmi *。 cmx
Php
最後に、PHP拡張機能を作成しましょう。 これを行うには、プロジェクトフォルダーに個別のphpフォルダーを作成します-拡張機能は独自のMakefileを作成し、上記で使用したMakefileを上書きするため、1つを別のものから分離します。 その中にcphppart.hファイルを作成します。 ファイルでは、標準モジュール関数、Chemlibクラス、およびget_group_of_camlグローバル関数を宣言しました。 再びstd :: vectorの使用を余儀なくされるため、PHPインクルードのexternを忘れないでください。 これで、cphppart.ccファイルに拡張機能を直接実装します。 まず、拡張機能、クラス、およびその機能に関する標準情報( こちらをご覧ください )。 次に、モジュール初期化関数-クラスがPHPに登録され、メソッドに加えてプロパティも受け取ります:
- PHP_MINIT_FUNCTION ( chemlib )
- {
- init_ocaml ( ) ;
- zend_class_entry chemlib_ce ;
- INIT_CLASS_ENTRY ( chemlib_ce、PHP_CHEMLIB_CLASS_NAME、chemlib_class_functions ) ;
- chemlib_class_entry = zend_register_internal_class ( & chemlib_ce TSRMLS_CC ) ;
- zend_declare_property_string ( chemlib_class_entry、 ( char * ) "name" 、 4 、 ( char * ) "" 、ZEND_ACC_PUBLIC TSRMLS_CC ) ;
- zend_declare_property_long ( chemlib_class_entry、 ( char * ) "position" 、8、0、ZEND_ACC_PUBLIC TSRMLS_CC ) ;
- zend_declare_property_long ( chemlib_class_entry、 ( char * ) "cycle" 、 5、0 、ZEND_ACC_PUBLIC TSRMLS_CC ) ;
- zend_declare_property_long ( chemlib_class_entry、 ( char * ) "group_type" 、10、0、ZEND_ACC_PUBLIC TSRMLS_CC ) ;
- zend_declare_property_long ( chemlib_class_entry、 ( char * ) "link" 、4、0、ZEND_ACC_PUBLIC TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "UndefPos" 、8、0 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "LeftPos" 、7、1 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "RightPos" 、8、2 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "UndefCycle" 、10、0 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "NoneCycle" 、9、1 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "AliphaticCycle" 、14、2 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "AromaticCycle" 、13、3 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "HeteroCycle" 、11、4 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "OrgGroup" 、 8 、 -2 TSRMLS_CC ) ;
- zend_declare_class_constant_long ( chemlib_class_entry、 ( char * ) "InorgGroup" 、 10 、 -1 TSRMLS_CC ) ;
- } ;
ご覧のとおり、グループ構造のCと同じプロパティをすべてクラスに設定し、さらにChemlib :: OrgGroupのような便利な定数を宣言しました。 デフォルトの機能、誰もが味わうことができる機能を追加します( こちら )。 CとPHPの間でグループを変換するための補助的な内部関数を使用したシーズン:
- zval * phpgroup_of_group ( group * gr )
- {
- zval * res ;
- ALLOC_INIT_ZVAL (解像度) ;
- object_init_ex ( res、chemlib_class_entry ) ;
- zend_update_property_string ( Z_OBJCE_P ( res ) 、res、 ( char * ) "name" 、 4 、gr- > name TSRMLS_CC ) ;
- zend_update_property_long ( Z_OBJCE_P ( res ) 、res、 ( char * ) "position" 、 8 、gr- > position TSRMLS_CC ) ;
- zend_update_property_long ( Z_OBJCE_P ( res ) 、res、 ( char * ) "cycle" 、 5 、gr- > cycle TSRMLS_CC ) ;
- zend_update_property_long ( Z_OBJCE_P ( res ) 、res、 ( char * ) "group_type" 、 10 、gr- > group_type TSRMLS_CC ) ;
- zend_update_property_long ( Z_OBJCE_P ( res ) 、res、 ( char * ) "link" 、 4 、gr- > link TSRMLS_CC ) ;
- 解像度を返す ;
- }
- グループgroup_of_phpgroup ( zval * gr )
- {
- グループres、def ;
- zval * x = zend_read_property ( chemlib_class_entry、gr、 ( char * ) "name" 、4、1 TSRMLS_CC ) ;
- if ( Z_TYPE_P ( x ) ! = IS_STRING )
- return def ;
- 解像度 name = estrdup ( Z_STRVAL_P ( x ) ) ;
- x = zend_read_property ( chemlib_class_entry、gr、 ( char * ) "position" 、8、1 TSRMLS_CC ) ;
- if ( Z_TYPE_P ( x ) ! = IS_LONG )
- return def ;
- 解像度 position = ( group_position ) Z_LVAL_P ( x ) ;
- x = zend_read_property ( chemlib_class_entry、gr、 ( char * ) "cycle" 、5、1 TSRMLS_CC ) ;
- if ( Z_TYPE_P ( x ) ! = IS_LONG )
- return def ;
li style = "font-weight:n