6

2本の線の交点を計算する関数を作りたいとしましょう。交点は常に定義されている、または一意であるとは限りません。それを関数のシグネチャに反映するにはどうすればよいですか?

私はこれらのオプションを考え出しました:

  1. bool getIntersectionPoint ( Line& a, Line& b , Point& result );

    線が平行な場合はfalseを返します。それ以外の場合はtrueを返し、結果を変数に書き込みます。

  2. Point getIntersectionPoint ( Line& a, Line& b );

    線が平行である場合、例外をスローします。

[更新]
2つの 関数を作成bool doLinesIntersect(const Line&, const Line&);Point twoLinesIntersection(const Line&, const Line&);、最初の関数がfalseを返した後でも、2番目の関数を呼び出すことができる場合。

4

7 に答える 7

4

私見、線交叉はオブジェクトを生成します、それが持っていることが正直である理由です

boost::variant<Empty, Point, Line> intersect(Line const & l1, Line const & l2)

とヘルパー関数のような

boost::optional<Point> getIntersectionPoint(Line const & l1, Line const & l2)

bool isParallel(Line const & l1, Line const & l2)

編集: ブーストライブラリを使用したくない場合は、簡単にアナログを作成できます。

struct intersection_result_t
{
  enum isec_t
  {
    isec_empty, isec_point, isec_line
  }

  intersection_result_t()
    : type_(isec_empty)
  {
    new (storage_) Empty();
  }

  intersection_result_t(Empty const & e)
    : type_(isec_empty)
  {
    new (storage_) Empty(e);
  }
  intersection_result_t(Point const & p)
    : type_(isec_point)
  {
    new (storage_) Point(p);
  }
...
  intersection_result_t(intersection_result_t & ir)
    : type_(ir.type_)
  {
    switch(ir.type_)
    {
      case isec_empty:
        new (storage_) Empty(*static_cast<Empty*>(ir.storage_));
      case ....
    }
  }
private:
  void destroy()
  {
    switch(type_)
    {
      case isec_empty:
        operator delete (static_cast<Empty*>(storage_), storage_);
      case ....
    }
  }
private:
  char storage_[MAX(sizeof(Empty), sizeof(Point), sizeof(Line))];
  isec_t type_;
};

などなど、さらにいくつかのスイッチが必要です。または、テンプレートを使用できます。オプションの場合は、initialized_代わりに使用type_して建設状態を追跡します。

于 2013-03-26T10:03:54.240 に答える
3

ulidtkoが示唆しているように、「ポイントである可能性がある」オブジェクトを返すとよいでしょう。C++では次を使用できますboost::optional

boost::optional<Point> getIntersectionPoint(const Line& a, const Line& b) {
    // ...
    if (there_is_zero_or_inifinty_points_of_intersection)
        return boost::optional<Point>();
    else
        return boost::optional<Point>(the_point_of_intersection);
}

boost::optional<Point>まるで。のように考えることができますPoint*。特に、クライアントは、返された交差点が適切なポイントであるかどうかを次のように照会できます。

boost::optional<Point> point = getIntersectionPoint(a, b);
if (point)
    // point "points to" a proper Point which can be retrieved as *point
else
    // point is "NULL", that is, there's no unique point of intersection

おかしなことに、のやる気を起こさせる例boost::optionalも幾何学的な問題です。boost::optional著者は幾何学的なソフトウェアを書いているので、これは偶然ではありません。;-)

C++標準の次のリビジョンでSTLに含める提案があることは言及する価値があります。optional

于 2013-03-26T10:25:12.143 に答える
0

平行線はエラーでも予期しないものでもありません。したがって、例外をスローすることは適切ではありません。

ところで、これは関数シグネチャとして推奨されます。

bool getIntersectionPoint(const Line& a, const Line& b, Point& result);

constを指定すると、関数が最初の2つの引数を変更しないことが明確になり、一時的に関数を呼び出すこともできます。

于 2013-03-26T09:44:03.540 に答える
0

抽象化(API)の観点からは、2つの無関係な関数があります。

bool doLinesIntersect(const Line&, const Line&);

Point twoLinesIntersection(const Line&, const Line&);

2番目の関数は、線が実際に交差している(そして同一線上にない)ことを前提としている必要があります。発信者を信頼できない場合は、前提条件が満たされていないことを示す例外をスローすることをお勧めします。

于 2013-03-26T09:47:30.750 に答える
0

2番目の関数は、おそらくPoint&ではなく、Point値(誰が所有しているのか)を返す必要があります。

または、3番目のオプションがあります。

Point getIntersectionPoint ( Line& a, Line& b, bool* ok );

'ok'にNULLポインターを指定した場合、共通部分がない場合はスローし、そうでない場合は'ok'の値にfalseを返します。

このような関数の場合、例外を完全に回避することをお勧めします。非交差は実際にはそれほど例外的ではなく、例外は実際には予期しないもののために予約する必要があります。交差しない線が期待できます。

boolを返すバージョン、またはbool引数を含むがスローしないバージョンを使用します。

編集よく使用される4番目のオプション:

std::pair<bool, Point> getIntersectionPoint ( Line& a, Line& b );
于 2013-03-26T09:50:46.887 に答える
0

この質問は、 C++で合計タイプを簡単にするための非常に良い動機です。

Haskellのような言語では、関数には次のシグネチャがあります。

getIntersectionPoint :: Line -> Line -> Maybe Point

ここでMaybe Point(関数の戻り型)は、基本的に2つの値を持つことができる型を意味します。NothingまたはJust p、ここpで、はPoint。です。

このような簡単な合計タイプが利用できると、すべてのアプローチが1つのアプローチに統合されるため、実際には質問はまったく不要になります。


編集:この回答は、Boostが簡単な合計タイプの機能を提供することをきちんと示しています。ありboost::optionalますboost::variant。甘い。

于 2013-03-26T09:58:00.517 に答える
-1

文脈が与えられなければ、人々は終わりなく議論するでしょう。

いくつかの内部で関数を使用したいのではないかと思います

fillWithColor(color c, set_of lines& figure);

そしてどういうわけかあなたはそれをするために使用getLinesIntersectionします。各呼び出しをチェックする必要がある場合は、コードが混乱するだけでなく、エラーをどう処理するかがわかりません。関数を使用して、呼び出し元に例外をキャッチさせるだけです。

他のコンテキストでは、次を実装できます。

bool doLinesIntersect(const Line&, const Line2&, Point &p);
Point getLinesIntersection(const Line&, const Line2&)
{
   Point p;
   If (! doLinesIntersect(Line, Line2,p) throw …;
   return p;
}

両方のアプローチは非常に有効です!!!

于 2013-03-26T10:21:46.683 に答える