10

学習演習として rebol で sed のようなユーティリティをハックするために、多くの長い文字列を検索することに興味があります。赤ちゃんのステップとして、私はキャラクターを探すことにしました:

>> STR: "abcdefghijklmopqrz"

>> pos: index? find STR "z"
== 18

>> pos
== 18

すごい!他のものを探しましょう...

>> pos: index? find STR "n"
** Script Error: index? expected series argument of type: series port
** Where: halt-view
** Near: pos: index? find STR "n"

>> pos
== 18

何?:-(

ええ、検索していた文字列に「n」はありませんでした。しかし、pos でテスト可能な "null" char を返すなど、何か賢明なことをする代わりに、インタプリタが爆破することの利点は何ですか?

私はこれをすべきだったと言われました:

>> if found? find STR "z" [pos: index? find STR "z"]
== 18

>> if found? find STR "n" [pos: index? find STR "n"]
== none

>> pos
== 18

本当に?文字列 TWICE を検索する必要があります。もう一度検索しても「安全」であることを確認するためだけに初めてですか?

そこで、次の 3 つの質問があります。

  1. ウィザードは検索機能をどのように実装しますか? これよりも優れた魔法のような方法があると思います....

  2. レッドはこれを変えるつもりですか?理想的には、 find が有効な文字列位置を返すか、文字列の終わりに達した場合は NULL を返す必要があると思います (NULL で区切られていると思いますか?)。NULL は FALSE であるため、非常に簡単な if テストがセットアップされます。

  3. 有効なインデックスを取得したら、最も CPU 効率の良い置換方法は何ですか? Rebol には非常に多くの選択肢があり (良いことです)、選択に行き詰まったり、次善の選択に行き詰まったりする可能性があります。

4

3 に答える 3

4

私はこれをすべきだったと言われました:

>> if found? find STR "z" [pos: index? find STR "z"]
== 18

>> if found? find STR "n" [pos: index? find STR "n"]
== none

>> pos
== 18

本当に?文字列 TWICE を検索する必要があります。もう一度検索しても「安全」であることを確認するためだけに初めてですか?

文字列を 2 回検索する必要はありません。しかしindex? (yes/no: を返さないため、おそらく将来の名前index-of)は NONE を返しません! NONE が与えられた場合の値! 入力。呼び出し元が整数位置を戻したいと想定し、それが得られない場合はエラーを発生させます。

ウィザードは検索機能をどのように実装しますか?

二重検索を排除するために、短絡評価を使用できます...

>> all [pos: find STR "z" pos: index? pos]
== 18

>> pos
== 18

>> all [pos: find STR "n" pos: index? pos]
== none

>> pos
== none

ただし、2 番目の変数を導入しないと、以前のpos. index代わりに変数を呼び出し、 pos が一時的であるとしましょう:

>> all [pos: find STR "z" index: index? pos]
== 18

>> index
== 18

>> all [pos: find STR "n" index: index? pos]
== none

>> index
== 18

中間式の任意のポイントでセット ワードをスローする機能は非常に強力です。そのため、複数の初期化 ( a: b: c: 0) などは言語の特別な機能ではなく、エバリュエーター モデルから外れるものです。

レッドはこれを変えるつもりですか?

index? index-of)NONEを返すことの利点はありそうにありません!NONE が与えられた場合の値! インプットは、寛容であることによって引き起こされる問題を上回ります。それは常にバランスです。

FIND は実際に期待どおりに動作することに注意してください。見つかった?見つかった位置を真の値、および NONE に変換する構文上の利便性にすぎません! 偽物に戻った。TRUE? を呼び出すのと同じです。(ただし、読むときはもう少し読み書きができます)。IF、UNLESS、または EITHER の条件で使用する必要はありません。NONE の結果を false のように処理し、任意の位置を true のように処理するためです。

有効なインデックスを取得したら、最も CPU 効率の良い置換方法は何ですか?

一番手っ取り早いのは、その位置にしがみついて、 と言ったことだろうchange pos #"x"。(内部的には「位置」は、独立したポインターではなく、インデックスとシリーズによって実装されます。そのため、オフセットの追加などをカウントしているマイクロ最適化の世界では、利点はそれほど重要ではありません... )

インデックスを使用した操作については、好きな方法を選択して、後でマイクロ最適化することをお勧めします。

私は個人的にはそれほど素晴らしいとは思いませんSTR/:index: #"x"が、文字は最も短いです.

STR/(index): #"x"同じことを行い、IMOの見栄えが良くなります。しかし、ソースコードの構造が少し爆発するという代償を払っています。それはSET-PATHです!パレン入りシリーズ!series の後に CHAR!... コードを保持している元の series "vector" にすべて埋め込まれています。フードの下には、地域性の問題があります。 そして、私たちは最近それがどれほど重要であるかを知っています...

一見素朴なPOKEが一番速いのかもしれません。 poke STR index #"x". 「2つではなく4つの要素」のように見えるかもしれませんが、パスケースの「2つの要素」は錯覚です。

Rebol では、推測するのは常に少し難しいので、データを収集する必要があります。いくつかの繰り返しテストを実行して確認できます。コード ブロックの時間を計るには、ビルトインを参照してくださいdelta-time

Red では、コンパイルされた形式は同等であるはずですが、何らかの形でこれが解釈されてしまうと、おそらく Rebol と同様のタイミングになるでしょう。

于 2015-02-28T03:56:24.580 に答える
3

HostileFork の回答がすべてを美しくカバーしていることは驚くことではありません。+1

私が定期的に使用するポイント1に代替ソリューションを追加したかっただけです:

>> attempt [index? find STR "z"]   
== 18

>> attempt [index? find STR "n"] 
== none

attemptRebol 2 & Rebol 3のオンライン ドキュメントattempt

于 2015-02-28T11:28:22.850 に答える
2

Red/Rebol での文字列の検索は非常に簡単で便利です。発生した問題について、詳細を説明します。

まず第一に、インタプリタはエラーメッセージの形で、何が間違っているかについての良いヒントを与えています: index? expected series argument of type: series port. これはindex?、間違ったデータ型で使用したことを意味します。どうしてこうなりました?検索が失敗した場合にfind関数が値を返すという理由だけで:none

>> str: "abcdefghijklmopqrz"
>> find str "o"
== "pqrz"
>> type? find str "o"
== string!

>> find str "n"
== none
>> type? find str "n"
== none!

したがって、検索が失敗しないことがわかっている場合を除きindex?、 の結果を直接使用することfind安全ではありません。とにかくインデックス情報を抽出する必要がある場合、安全な方法はfind最初の結果をテストすることです:

>> all [pos: find str "o" index? pos]
== 14
>> all [pos: find str "n" index? pos]
== none
>> if pos: find str "o" [print index? pos]
== 14
>> print either pos: find str "n" [index? pos][-1]
== -1

これらは、ニーズに応じて、それを達成するための安全な方法の例にすぎません。はまたはの条件付きテストnoneとして機能するため、そのような場合に使用する必要はありません。falseifeitherfound?

ここで、混乱を招いた中心的な問題に光を当てましょう。

Rebol 言語には、データ型の派生元seriesであるa と呼ばれる基本的な概念があります。string!シリーズを正しく理解して使用することは、Rebol 言語を慣用的な方法で使用できるようにするための重要な部分です。シリーズは、他の言語では通常のリストや文字列のようなデータ型のように見えますが、同じではありません。シリーズは次のもので構成されています。

  • 値のリスト (文字列の場合は文字のリスト)
  • 暗黙的なインデックス (簡単にするためにカーソルと呼ぶことができます)

次の説明は文字列のみに焦点を当てていますが、同じ規則がすべての系列データ型に適用されます。以下の例では、暗黙的なインデックスを整数としてindex?表示するために関数を使用します。

デフォルトでは、新しい文字列を作成するとき、カーソルは先頭の位置にあります。

>> s: "hello"
>> head? s
== true
>> index? s
== 1

ただし、カーソルを移動して、文字列内の他の場所を指すことができます。

>> next s
== "ello"
>> skip s 3
== "lo"
>> length? skip s 3
== 2

ご覧のとおり、カーソルを移動した文字列はカーソル位置から表示されるだけでなく、他のすべての文字列 (または系列) 関数もその位置を考慮します

さらに、文字列を指す各参照にカーソルを設定することもできます。

>> a: next s
== "ello"
>> b: skip s 3
== "lo"
>> s: at s 5
== "o"
>> reduce [a b s]
== ["ello" "lo" "o"]
>> reduce [index? a index? b index? s]
== [2 4 5]

ご覧のとおり、特定の文字列 (または系列) に対して必要な数の異なる参照を持つことができ、それぞれに独自のカーソル値がありますが、すべて同じ基になる値のリストを指しています。

シリーズ プロパティの 1 つの重要な結果:他の言語で行うように、文字列 (および他のシリーズ) を操作するために整数インデックスに依存する必要はありません。必要な計算を行うためにシリーズ参照に付属するカーソルを活用するだけで済みます。 、コードは短く、きれいで、非常に読みやすいものになります。それでも、シリーズでは整数インデックスが役立つ場合がありますが、必要になることはほとんどありません。

それでは、文字列を検索するユースケースに戻りましょう。

>> STR: "abcdefghijklmopqrz"
>> find STR "z"
== "z"
>> find STR "n"
== none

必要なのはそれだけです。実行する必要があるほとんどすべての計算に結果の値を使用するために、インデックス位置を抽出する必要はありません。

>> pos: find STR "o"
>> if pos [print "found"]
found
>> print ["sub-string from `o`:" pos]
sub-string from `o`: opqrz
>> length? pos
== 5
>> index? pos
== 14
>> back pos
== "mopqrz"
>> skip pos 4
== "z"

>> pos: find STR "n"
>> print either pos ["found"]["not found"]
not found
>> print either pos [index? pos][-1]
-1

以下は、整数インデックスを明示的に使用せずに部分文字列を抽出する方法を示す簡単な例です。

>> s: "The score is 1:2 after 5 minutes"
>> if pos: find/tail s "score is " [print copy/part pos find pos " "]
1:2

少し練習すれば (このような実験にはコンソールが最適です)、単純な整数インデックスだけでなく、Rebol 言語でシリーズに完全に依存することがいかに簡単で効率的であるかがわかります。

さて、あなたの質問に対する私の見解は次のとおりです。

  1. find上記のように、シリーズと機能を適切に使用するだけで、魔法は必要ありません。

  2. 赤はそれを変えるつもりはありません。系列は、Rebol 言語をシンプルかつ強力にする基礎です。

  3. changeただし、長い文字列を操作するための置換が多数ある場合、元の文字列を変更する代わりに新しい文字列を再構築すると、置換文字列がないときにメモリチャンクを移動することを回避できるため、パフォーマンスが向上することがよくあります。交換する部品と同じサイズ。

于 2015-12-14T08:43:00.967 に答える