タグ別アーカイブ: note on programming language cpp 4th edition

「プログラミング言語C++第四版」について気が付いたことなど (22)

意味がよく分からない訳、そこまで単純じゃないという誤訳 …


42. 4   タスク ベース の 並行 処理

本章のここまでは、並行タスクを実行するためのメカニズムを中心に解説してきた。すなわち、thread、競合条件の回避、threadの同期である。この点に集中すると、並行タスクを実行する実際のタスクへの注意が散漫になってしまうのが多いことが分かっている。本節では、単純な部類のタスクに焦点を当てる。具体的には、1個の引数を受け取って、1個の結果を返すタスクである。


原文:

So far, this chapter focused on the mechanisms for running concurrent tasks: the focus was on threads, avoiding race conditions, and synchronizing threads. For many concurrent tasks, I find this focus on mechanisms distracting from the real task (sic!) of specifying concurrent tasks. This section focuses on specifying a simple kind of task: a task that does one thing given arguments and produces one result.


試訳:

本章のここまでは、並行タスクを実行するためのメカニズムにフォーカスを当てて解説してきた。すなわち、thread、競合条件の回避、threadの同期である。多くの並行処理の現場で、このような細かなメカニズムに対する集中は並行処理を記述するという本来の(文字通りの!)タスク(=作業)からの逸脱をもたらしがちである。本節では、単純な部類のタスクに焦点を当てる。それは幾つかの引数を与えられて一つのことを実行し、一つの結果を生成するタスクである。


考察:

後ろの方の訳は arguments が複数であることを見落としており、かつ does one thing が訳されていない。”one thing given arguments” を「1個の引数を受け取って」としているのかもしれないが、それはちょっと間違った捉え方。given はtaskにかかる形容詞的過去分詞、たぶん。

「プログラミング言語C++第四版」について気が付いたことなど (21)

よく似てるけど…


42.3.4  条件変数

…( condition_variable の表の中ほど)…

cv.wait(lck) 現在のスレッドがlckを所有していなければならない。自動的に lck.unlock()を呼び出してブロックする。イベント通知を受けるか、または “擬似的に通知を受ける”とブロック解除する。ブロック解除時に lck.lock() を呼び出す

原文:

cv.wait(lck) lock must be owned by the calling thread;
atomically calls lck.unlock() and blocks;
unblocks if notified or “spuriously”;
when unblocked calls lck.lock()

考察:

atomically automatically と空目しているように思われます。
この後の x=cv.wait_until(lck,tp)でも同様にatomically自動的に になってます。

尚、”spuriously” は、(notifyされなくても)処理系によって擬似的に待機解除されることがあるということなので、待機解除されたからと言って条件が成り立っているとは限らない点に注意が必要です。

 

「プログラミング言語C++第四版」について気が付いたことなど (20)

こんなもんで十分だ、と言ってるんだけど …


42.3.1.3 timed_mutex と recursive_timed_mutex

…(略)…

ここでは、画像イメージを適切な速度(この場合100ミリ秒)で更新できない場合は、新しい画像イメージをユーザに見せたほうが好ましいと仮定している。さらに、一連の画像イメージの中から1枚分の画像イメージが欠けていても、まず気付かれないだろうとも仮定している。すなわち、実際には、もっと複雑な処理が必要だ。

42. 3. 1. 4   lock_ guard と unique_ lock


原文:

The assumption here is that if the image cannot be updated reasonably fast(here, in 100 milliseconds), the user would prefer a newer version of the image. Further, it is assumed that missing an image in a sequence of updated images will rarely be noticed, so that a more complicated solution is not needed.


試訳:

ここでの仮定はもし画像が妥当な速さで(この例の場合100ミリ秒)更新できないなら、ユーザーは次のコマの画像が表示されるのを好むだろうということである。更に言うと、一連の更新画像中の一枚が欠けていてもまず気付かれることは無いと思われ、従ってこれ以上複雑な対策は不要である。


考察:

“新しい画像イメージ” は 要するに動画の次のコマという意味。
また、「すなわち」以降は意味が逆。たぶん、not を見落としている。

「プログラミング言語C++第四版」について気が付いたことなど (19)

ロックフリープログラミングにはトライするなと言ってるのに …


41.3  アトミック性

…(略)…

単純なアトミックカウンタは例外だが、ロックフリープログラミングは、専門家のためのものだ。言語の仕組みを理解するだけではなく、特定のマシンアーキテクチャの詳細な把握と、専門の実装技法の知識が欠かせない。本書で提示する内容だけに基づいて、ロックフリープログラミングを試みてはいけない。ロック技法を把握した上で論理的に高度なロックフリー技法を活用すれば、デッドロックやスタベイションなどの古典的なロック問題は発生しない。


原文:

…. Do not try lock-free programming with only the information provided here. The primary logical advantage of lock-free techniques over lock-based techniques is that classical locking problems, such as deadlock and starvation, cannot happen.


試訳:

… 本書で提供されている情報だけでロックフリープログラミングを試みないように。ロックベースの技法に対するロックフリー技法の主要な理論的優位性は、デッドロックやスターベイションのような古典的なロッキングの問題が発生し得ないということ(だけ)だ。


考察:

生半可な知識でトライするなと言ってるくせに、その直後にすばらしい技法だと勧めているような物言いは不自然に思われます。「論理的に高度なロックフリー技術」という捉え方は「logical advantage of lock-free techniques」の訳としては間違っていますし(それを言うなら logically advanced lock-free techniques ?)、それを「活用すれば」とか、どこにも書いてないし。原文で言ってるのはそういうことではないです、たぶん。

「プログラミング言語C++第四版」について気が付いたことなど (18)

そんな大げさに言わなくても … (オマケ付き)


38.4.5.3 ユーザ定義の操作子

….(略)….

さて、この宣言によって、<<と()の組合せが、単一の3項演算子に統合されることに注意しよう。cout<<sci4{d}は、実際の処理を実行する前に、ostreamと書式と値を、単一の関数に集約する

38.5  ストリーム反復子


原文:

Note that these declarations make the combination of << and () into a ternary operator;
cout<<sci4{d} collects the ostream, the format, and the value into a single function before doing any real computation.


試訳:

これらの宣言が << と () の組み合わせを三項演算子にすることに注意しよう。cout<<sci4{d} は、実際の処理を実行する前に、ostreamと書式と値を単一の関数(オブジェクト)にまとめる


考察:

Form sci4; という宣言は見当たらないけどどこかにあるとして(名前から見て sci4.scientific().precision(4) も実行されているだろう)、sci4のForm::operator()を引数dと共に呼び出す(sci4{d})ことで新たに値dと結び付けられたBound_form型のインスタンスを作り出し、それを cout::operator<<(ostream&,const Bound_form&) に渡す、という流れ。

sci4{d} は sci4(d) と書いても同じ。

Form sci4;
Bound_form bf { sci4(d) };
cout << bf;

と分けて書いても同じ(dはどこかで宣言されているものとして)。

オマケの補足: なぜsci4{d} と書くことで Form::operator() が呼び出されるのか?
ぱっと見不思議な書き方で一瞬ミスプリントじゃないかと思ったくらいですが、これはどうも、{}の初期化リストからコンパイラがinitializer_list<>を自動生成するのと、作成されたinitialize_listの要素が一個の場合は引数一個のoperator()関数の呼び出しに変換されるようで、そのため、引数一個の場合に限りこのような書き方が出来るみたいです。(違うかも、実は自信ないです)
引数が無い場合や二個ある場合は{}を使った書き方はコンパイルエラーになってしまいます。operator()の呼び出しで汎用的に()の代わりに{}が使える訳ではない模様。

 

「プログラミング言語C++第四版」について気が付いたことなど (17)

向きが逆のような気がするのだけれども …


38. 4. 4   ストリーム の 状態

…(略)…

tie()は、あるストリームからの入力を行う前に、それに結び付いたストリームからの出力を先に行うことを保証するものだ。たとえば、coutにはcinが結び付けられている:


原文:

The tie() is used to ensure that output from a tied stream appears before an input from the stream to which it is tied. For example, cout is tied to cin:


考察:

cout に cinが結び付けられているのではなくて、cout は cinに結び付けられている、つまり、cin には coutが結び付けられている、ということ。

cinがcoutの情報を持っているから、cinからの入力の実行前にcoutの出力状態をチェックすることが出来るわけ。coutがcinの情報を持っていても cin側からcoutの状態を見ることは出来ない。

ちなみに、この後ろの方では “so had cout not been tied to cin” の訳として(正しく)「cout が cinに結び付けられていなければ」となっている。

……….

とは言うものの、これはちょっと細かいところにこだわり過ぎているかも知れない。というのは、C++ referenceサイトのstd::basic_ios::tieの説明 https://en.cppreference.com/w/cpp/io/basic_ios/tie には(cout is tied to cin and cerr ではなく)下記のように書かれているから。実は結び付きの向きなんて誰も気にしてないのかも 🙂

Notes

By default, the standard streams cin and cerr are tied to cout.

 

「プログラミング言語C++第四版」について気が付いたことなど (16)

どうでもいいんだけど、命名法の違いの理由が気になる …


33.4  関数オブジェクト

(算術演算向け関数オブジェクト)

f=plus<T>{}        xとyの型がTであれば、f(x,y) は x+y を意味する。
f=minus<T>{}       xとyの型がTであれば、f(x,y) は x-y を意味する。
f=multiplies<T>{}  xとyの型がTであれば、f(x,y) は x*y を意味する。
f=divides<T>{}     xとyの型がTであれば、f(x,y) は x/y を意味する。
f=modulus<T>{}     xとyの型がTであれば、f(x,y) は x%y を意味する。
f=negate<T>{}      xの型がTであれば、f(x) は -x を意味する。

考察:

multiplyとdivideだけに三単元もしくは複数形っぽいsが付いているのは何故?

一瞬、この二つは動詞だからかと思った(plus/minusは前置詞、modulusは名詞)けど、同じく動詞のnegateはnegatesにはなっていない … 。

関数オブジェクトのテンプレートの名前なので、名詞扱いにしているのだろうとは思うのだけど。negateの対象は一つだからsが付かないと考えるなら、multipliesとdividesのsは複数形のsと見ればいいのかしらん?

いずれにしてもこれはこの本ではなくてC++のライブラリの仕様の問題ですな。

 

「プログラミング言語C++第四版」について気が付いたことなど (15)

間違ってるわけではないのだけど、あらぬ誤解を招きそうな …


32.6.1   2分探索

…(略、(14)の続き)…

不思議に感じられるかもしれないが、2分探索アルゴリズムでは、ランダムアクセス反復子は不要であり、前進反復子で十分である。


原文:

Curiously enough, the binary search algorithms do not require random-access iterators: a forward iterator suffices.


考察:

forward iterator で実装できるのは確かなのだけれども、binary_search()は random-access iteratorが与えられた時はそれを使って探索を効率化することになっている。なので、「不要」と言い切っちゃうのは抵抗があるし、「前進反復子で十分」というのはあらぬ誤解を招くんじゃないだろうか。というわけで。


試訳:

奇妙に思えるかもしれないが、二分探索アルゴリズムにはランダムアクセス反復子は必須ではない。前進反復子でも用は足りる。

 

「プログラミング言語C++第四版」について気が付いたことなど (14)

微妙に怪しい訳 …


32.6.1   2分探索

…(略、(13)の続き)…

同様のエラー通知方法は、upper_ bound() や equal_ range() でも行われている。そのため、これらのアルゴリズムを用いると、ソートずみシーケンスに対して新規要素を挿入しても、ソートされた状態を維持できる。そのためには、返却されたpairのsecondの直前に新規要素を挿入する。


原文:

This way of reporting failure is also used by upper_bound() and equal_range(). This means that we can use these algorithms to determine where to insert a new element into a sorted sequence so that the sequence remains sorted: just insert before the second of the returned pair.


試訳:

同様のエラー通知方法はupper_bound()とequal_range()でも用いられている。このことは、ソート済み状態を維持したままでシーケンスに新しい要素を追加するにはどこに挿入すれば良いかを決めるためにこれらのアルゴリズムを利用できることを意味する。返却されたpairのsecondが指す要素の直前に挿入すればよい。


考察:

全体としての意味は合ってるんですけどね。ここで使われている so that は “~するために” の意味で訳したほうが分かり易いのではないかと。

「プログラミング言語C++第四版」について気が付いたことなど (13)

lower_boundとupper_bound、ややこしい仕様ではあるのだけれども …


32.6.1    2分探索

…(略)…

lower_bound(first,last,k)は、kを探索するのではなく、kより大きなキーをもつ先頭の要素を指す反復子を返す。ただし、kより大きなキーをもつ要素が存在しなければlastを返す。同様のエラー通知方法は、upper_bound()やequal_range()でも行われている。


原文:

If lower_bound(first,last,k) doesn’t find k, it returns an iterator to the first element with a key greater than k, or last if no such greater element exists. This way of reporting failure is also used by upper_bound() and equal_range().


試訳:

lower_bound(first,last,k)がkと一致するキーを見つけられない場合はkより大きなキーを持つ最初の要素を指す反復子を、その様な要素が存在しない場合はlastを返す。同様のエラー通知方法は、upper_bound() と equal_range() でも用いられている。


考察:

ややこしいが、(探索に成功した場合)lower_boundはkと一致する最初の要素を返し、upper_boundはkより大きなキーを持つ最初の要素を返す。なので「kを探索するのではなく」だとupper_boundの仕様になってしまう。たぶん、一覧表の方に出ているlower_bound関数の仕様(p=lower_bound(b,e,v)  pは、[b:e)内で最初に出現するvを指すようになる)を忘れていて、かつ文頭の”If”を見落としている。