- PR -

お手本になるようなソースコード

投稿者投稿内容
ひろれい
ぬし
会議室デビュー日: 2006/03/02
投稿数: 486
お住まい・勤務地: 万博開催地
投稿日時: 2007-05-14 10:43
引用:

わちゃさんの書き込み (2007-05-13 18:15) より:

さて、ひろれいさんのコードですが、{100,50,49,48,47,1} で、242 円の場合に、
100 + 47*3 + 1 になってしまったようでした。
( 100 + 48 + 47 * 2 が最小 )


あぁ!おっしゃる通りですね・・・orz
単純ループだと、このパターンのように小さい数字の方でまとめた方が最小枚数になる場合の対応は難しいですね。Ver.3 は僕の足りない頭脳では無理だなぁ
ギブアップです。ごめんなさい。

しかし、わちゃさんのソースは速いですねぇ
ビックリしました。どんな技術手法を採用されているのか時間がある時に勉強したいと思います。とても勉強になります。ありがとうございます。

引用:

ぼのぼのさんの書き込み (2007-05-13 02:42) より:

確かに保守性の面では厳しいですが、実行速度最優先なら、十分にお手本として成立すると思います。
実際に試すことは非常に重要ですが、今回の場合はあまりに明らかだと思います。
繰り返し処理を殆ど使ってませんから。
唯一繰り返し処理が使われているのはCountPaperとCountCoinsの再帰の部分ですが、
この部分の繰り返しの最大数は金種の配列長と同じですから、微々たるものです。



例えば、0.5 秒 と 1.5 秒 ではその差が 3 倍 ですが体感的にはそれ程の差を感じないかなぁ、と思いましたので。
また、Ver.2 であればそこまでの差は発生しないのでは、とも思いました。

確かに、おっしゃる通り、実行速度最優先なら問題ないと思いますし、前レスでも書いていますが、自分のアプローチ方法では限界があるため、Anonymous Coward さんのアプローチも十分現実的な方法だと思います。
びしばし
大ベテラン
会議室デビュー日: 2002/03/13
投稿数: 181
投稿日時: 2007-05-14 10:46
引用:

びしばしさんの書き込み (2007-05-12 11:11) より:
・最初に最大金額のもの(10000円)と2番目のもの(8000円)との最小公倍数(40000円)を確保できるだけ確保してしまって、残額(40000円未満)についてのみ探索を行う。


訂正。40000円は「すべての金種の最小公倍数」ですね。

金種のパターンによっては、その最小公倍数が大きすぎて内部で表現できなくなってしまったり(インフレ ?)、
最小公倍数未満の探索ですらメモリ不足で実行できなくなったりしてしまうことが容易に想像できますが、
こうなってくると、外部メモリ(いっそDB)を利用してしまうほうが現実的かもしれません。何度も繰り返し使えますし
わちゃ
大ベテラン
会議室デビュー日: 2005/12/05
投稿数: 162
お住まい・勤務地: 東京
投稿日時: 2007-05-14 20:01
引用:

なお、バグ報告になりますが、金種リストが {30, 10, 7} の時に 135 を計算しようとすると IndexOutOfRangeException が出るようです。(ちなみにこの解は 30*3 + 10*1 + 7*5 です。)



ぬぅ、たしかに、よくないですね。

unibon さんのコードでは、ちゃんと大小関係までチェックされていて、
あぁ、こうするのがいいんだなぁと、ちょっと関心しておりました。

こういうので、チェックしないから、43 がダブってても、気づかないん
ですね、、、orz


自分のコードは、1円硬貨がある前提でした。
一応、条件分岐で、1行足すと、先ほどの 135 円のも大丈夫にはしましたが、
こういうあたりは、自分のエラーチェックに対する認識に少なさを
感じてしまいますね。

引用:

金種計算の際に、金種の最小が1円かどうかで、自由度が大きく違ってくるような気がします。1円玉が存在することにより、総額の微調整が格段にしやすくなりそうな感じですので。このあたりはアルゴリズムの検討の際に大きく関わってくることなのでしょうか。気になります。



たしかに、unibon さんのような最小公倍数などを使う場合は影響しそうですね。
私のアルゴリズムだと、単純に総当たりなので、あまり関係はないような気がします。

とりあえず、コメントを入れてはいたのですが、分かりにくかった部分もあるようですので、
簡単にアルゴリズムの紹介をしたいと思います。


基本は、再帰呼び出しによる総当たりがベースです。
ループが、金種の数だけあると思うと分かりやすいかもしれません。

そのなかで、最大の金種から、1枚ずつ変更しながら、残った金額を
それ以下の金種で、何枚でできるかを探索していきます。


その中で、主な最適化のうち、二つを紹介したいと思います。

一つ目の最適化は、
コード:
' 次の金種で、自明に枚数が大きい場合は、ループ終了
If (試行金額 \ 金種リスト(開始金種 + 1)) + 今回金種枚数 > 最適枚数 Then
    Exit For
End If


の部分です。

例えば、実際の硬貨で、401 円を支払うのに、まずは、100円玉を 4 枚使った場合を
探索します。すると、100 x 4 + 1 で、5 枚でできる事が分かります。
次に、100円玉を減らして 3 枚にした場合を考えると、残額は 101 円になり、
次の金種である 50 円玉が少なくとも 2 枚は必要になります。
これでは、100 円玉を減らす意味がありません。

つまり、その次に大きい金種以降を、どう組み合わせてもすでに知っている枚数よりも
多い枚数が必要になってしまいます。
そうであれば、それ以上、100円玉を減らす理由がありません。
その場合は、ループを抜けてしまいます。

ただ、この方法の場合、404 円などの場合に、ほとんど最適化がききません。
つまり、100円玉を 4 枚使った場合、100 x 4 + 1 x 4 の8枚が必要ですが、
100円玉を減らして 3 枚にした場合を考えると、残額は 104 円になり、
50 円玉以降の金種で、運がよければ 3 枚でできるかもしれませんので、
(実際、27円硬貨があれば、104円を3枚でできます)
100円玉を1枚ずつ減らして、全パターンを探索してしまいます。


二つ目の最適化は、
コード:
' まずは、キャッシュに計算済みの結果がないか調べる
If キャッシュ.ContainsKey(金額 * 金種リスト.Length + 開始金種) Then
    Return DirectCast(キャッシュ.Item(金額 * 金種リスト.Length + 開始金種).Clone(), Integer())
End If


の部分です。

再帰定義される関数は、末端において、まったく同じパターンによる呼び出しが多い事がしばしばあります。
特に、Ver.2 で、63999円などを計算する場合、2000円札以上の組み合わせには探索の価値がありますが、
1000円札を使った後の 999円以下の組み合わせについては、すでに計算済みであるのに、何度も
呼び出されてしまいます。

そのようなパターンを少なくするために、すでに計算済みのパターンであれば、キャッシュで記憶して
おくようにしました。
びしばしさんの外部 DB に入れておくのに似たイメージかもしれません。

ただ、このキャッシュは、要素数が、最大で(金額 × 金種数)に膨れ上がる可能性がありますので、
なんらかの限度を設けるようにした方がいいのかもしれません。


unibon さんの最大枚数を使ったアルゴリズムも興味があるのですが、なかなか
自分のコードにうまく適用できませんでした。


ちなみに、そこそこ速くはなったのですが、
{100, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 1} という金種で、404 円を計算しようとすると、
再帰呼び出しが、2500回ぐらいにまで増えてしまったので、ちょっとへこんでます。


誰か、つぎのお題ありませんか?
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2007-05-14 22:24
次のお題というより、つづきなんですけど。

私の場合、通貨だと、どうしても
「1円は絶対あるだろ」
とか、クレカのポイントにしても、
「現実的に大きい素数を使うことなんかありえねー」
とかの意識が頭から離れないので、
お題Ver.4を考えてみました。

──────────────────────────────────────────
お題Ver.4

あなたはあるゲームに参加することになりました。
このゲームのルールは以下の通りです。

  1. プレイヤーはある指定された金額の現金を持ってお店に入り、好きなように買い物をする
  2. お店から出てきた時点で、より所持金の残額が少ない方が勝ち
  3. 所持金の残額が同じ場合、買った商品の数がより少ない方が勝ち
  4. 所持金の残額も商品の数も同じ場合、より高額な商品を多く持ってる方が勝ち

例)最初の所持金が30000円でA〜Dの買い方が以下の通りだった場合
 A:29999
 B:25000+4000+500+500
 C:25000+3000+2000
 D:25000+2500+2500
順位はCDBA

最初の所持金とお店で扱っている全商品の価格(重複するものはあらかじめマージ)を
入力とし、確実に1位になれるような買い方を求めるプログラムを作ってください。
──────────────────────────────────────────

Ver.3すら自分で解けてないのに難易度を上げてしまった…
自分で自分の首を締めてどーする>俺w
sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2007-05-15 09:56
クソ長いプログラムを貼っても怒られないスレッドはここですか
Ver.3、Ver.4対応です。 前回のRuby版の拡張ですが、こんどはJavaで。

コード:

import java.util.*;

/** 動的計画法+分割統治法で金種計算 */
public class CurrencyCalc {
/** @param args 先頭:目標金額、残り:金種 (省略可) */
public static void main(String[] args) {

long[] defaultData = { 123456789, 10000, 5000, 2000,
1000, 500, 100, 50, 10, 5, 1 };
long[] argsl = toLongArray(args);

long amount = (argsl.length == 0) ? defaultData[0] : argsl[0];
long[] data = (argsl.length < 2) ? defaultData : argsl;

NavigableSet<Currency> set = new TreeSet<Currency>();
for (int i = 1; i < data.length; i++) {
set.add(new Currency(data[i]));
}

new CurrencyCalc(amount, set).exec();
}

/** 分割された問題を解く */
private static long calcDividedProblem(long dAmount,
NavigableSet<Currency> mycurrencies,
NavigableMap<Currency, Long> map) {

// 対象の通貨が1種類なら割り算でOK
if (mycurrencies.size() == 1) {
Currency cur = mycurrencies.first();
map.put(cur, dAmount / cur.price);
return dAmount % cur.price;
}

// 対象の金額と使用する全ての通貨の最大公約数で
// 割ることにより、問題を小さくする
long unit = gcd(dAmount, gcd(mycurrencies));

// 動的計画法のテーブル
int du = (int) (dAmount / unit) + 1;
long[] gain = new long[du];
Currency[] choice = new Currency[du];
// 動的計画法を解く
for (Currency cur : mycurrencies) {
int size = (int) (cur.price / unit);
long score = ((cur.price /unit) << 16) - 1;
for (int i = size, end = du; i < end; i++) {
int space = i - size;
long old = gain[space];
long newScore = old + score;
if (gain[i] <= newScore) {
gain[i] = newScore;
choice[i] = cur;
}
}
}

// それぞれの通貨の使用数と合計金額を数える。
long asum = 0;
for (int i = du - 1; i > 0;) {
Currency cur = choice[i];
if (cur != null) {
map.put(cur, map.containsKey(cur) ? map.get(cur) + 1 : 1);
i -= cur.price / unit;
asum += cur.price;
} else { // ぴったりにならない場合
i--;
}
}
return dAmount - asum;
}

/** 通貨の集合を分割する。 */
private static List<NavigableSet<Currency>> divide(NavigableSet<Currency> set) {
List<NavigableSet<Currency>> list
= new ArrayList<NavigableSet<Currency>>();
Currency noneMulti = null;
Currency head = new Currency(set.first().price + 1);
for (Currency cur : set) {
boolean b = (noneMulti == null)
|| isFactorOfCurrencies(cur, set.subSet(
noneMulti, true, cur, false));

if (b && isMultipleOfCurrencies(cur, set)) {
list.add(set.subSet(head, false, cur, true));
head = cur;
} else if (noneMulti == null) {
noneMulti = cur;
}
}
if (head != set.last()) {
list.add(set.tailSet(head, false));
}

return list;
}

/** ユークリッド互助法 */
private static long gcd(long m, long n) {
return n == 0 ? m : m % n == 0 ? n : gcd(n, m % n);
}
/** 通貨の集合の最大公約数を求める */
private static long gcd(NavigableSet<Currency> mycurrencies) {
long cmn = mycurrencies.first().price;
for (Currency c : mycurrencies) {
cmn = gcd(cmn, c.price);
}
return cmn;
}

/** すべての上位通貨の約数になっているか? */
private static boolean isFactorOfCurrencies(Currency cur,
NavigableSet<Currency> set) {
for (Currency cur2 : set.headSet(cur)) {
if (cur2.price % cur.price != 0)
return false;
}
return true;
}

/** すべての下位通貨の倍数になっているか? */
private static boolean isMultipleOfCurrencies(Currency cur,
NavigableSet<Currency> set) {
for (Currency cur2 : set.tailSet(cur)) {
if (cur.price % cur2.price != 0)
return false;
}
return true;
}

/** 使用する通貨の絞込み */
private static NavigableSet<Currency> narrowing(long amt,
NavigableSet<Currency> set) {
// 求める金額より大きい通貨と、
// 求める金額とその上位通貨の全て約数
// になっている通貨より小さい通貨は不要。
Currency factor = set.last();
for (Currency cur : set) {
if (amt % cur.price == 0 &&
isFactorOfCurrencies(cur, set)) {
factor = cur;
break;
}
}
return set.headSet(factor, true);
}

/** 文字列配列→整数配列
* @throws NumberFormatException */
private static long[] toLongArray(String[] argv) {
long[] ary = new long[argv.length];
for (int i = 0; i < ary.length; i++) {
ary[i] = Long.parseLong(argv[i]);
}
return ary;
}

private final long amount;
private final NavigableSet<Currency> currencies;

public CurrencyCalc(long amount, NavigableSet<Currency> currencies) {
this.amount = amount;
this.currencies = currencies;
}

/** 金種を計算する */
public NavigableMap<Currency, Long> calc() {

List<NavigableSet<Currency>> divideCurrencies = divide(narrowing(amount, currencies));
List<NavigableMap<Currency, Long>> res
= new ArrayList<NavigableMap<Currency, Long>>(
divideCurrencies.size());

// 結果用のマップ
NavigableMap<Currency, Long> map = new TreeMap<Currency, Long>();

long rem = amount;
for (NavigableSet<Currency> cs : divideCurrencies) {
long newReminder = rem % gcd(cs);
long dAmount = rem - newReminder;
if (dAmount == 0) continue;

//System.out.printf("分割問題 %10d円 : %s\n", dAmount, cs);
long calc2 = calcDividedProblem(dAmount, cs, map);
assert (calc2 == 0 || divideCurrencies
.get(divideCurrencies.size() - 1) == cs);

rem = newReminder + calc2;
if (rem == 0) break;
}

return map;
}

/** 実行 */
public void exec() {
System.out.printf("%d円 を %s で支払う。 \n", amount, currencies);
printResult(calc());
}

/** 結果表示 */
public void printResult(SortedMap<Currency, Long> map) {

long sum = 0;
long asum = 0;
for (Currency cur : map.keySet()) {
Long count = map.get(cur);
System.out.printf("%8d × %d\n", cur.price, count);
sum += count;
asum += cur.price * count;
}

System.out.printf("合計 : %d円 %d枚(個)\n", asum, sum);

if (amount > asum) {
System.out.printf("不足 : %d円\n", amount - asum);
}
}

/** 通貨クラス */
static class Currency implements Comparable<Currency> {
final long price;
public Currency(long price) {
if (price < 0)
throw new IllegalArgumentException();
this.price = price;
}
public int compareTo(Currency o) {
return (price < o.price) ? 1 :
(price > o.price) ? -1 : 0; // 昇順
}
@Override
public String toString() {
return price + "円";
}
}
}



実行例
コード:

>java algo.currency.CurrencyCalc 3000000 9000 4590 2300 980 650 200
3000000円 を [9000円, 4590円, 2300円, 980円, 650円, 200円] で支払う。
9000 × 333
650 × 4
200 × 2
合計 : 3000000円 339枚(個)



目標金額が高々100万円程度なら、どんな金種(商品)の組でもほぼ一瞬で解けます。現実的な金種なら、かなり大きな目標金額でもOK。ダメなときはjava.lang.OutOfMemoryErrorかjava.lang.NegativeArraySizeExceptionになります。(ダメなときも一瞬)

元々作ってあったやつで、Ver.4対応で変えたところは、途中の"<"を"<="に変えて(高い商品を優先)、と「お釣り」でなく「不足」を求めるように変更ぐらいです。

コメントとかは端折り気味ですがご勘弁を。(さらに長くなるので…)

修正:枚数(商品数)の差がすごく大きいときに、金額をぴったりにすることより、枚数を減らすことを優先してしまうバグを修正。(Scoreクラスの導入)
そしたら、ちょっと処理性能が悪くなった…。

再修正:処理速度の劣化がひどいので、やっぱりScoreクラスは廃止。枚数の差が65536を超えると間違えますが…。

[ メッセージ編集済み 編集者: sawat 編集日時 2007-05-15 11:58 ]

[ メッセージ編集済み 編集者: sawat 編集日時 2007-05-15 12:01 ]

[ メッセージ編集済み 編集者: sawat 編集日時 2007-05-15 12:42 ]
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2007-05-17 08:18
お題Ver.4の解が、ようやく形になってきますた

自分でバージョンアップさせたお題を解けないのが悔しくて、
仕事の合間にちょこちょこ弄くってたのですが、
なんとか速度的にもツカイモノになるレベルになってきました。

数字の組み合わせによっては、sawatさんのプログラムの方が明らかに速い場合もあるのですが、
逆に別の数字の組み合わせだと、sawatさんのプログラムがOutOfMemoryErrorを投げるのに対し、
こっちはちゃんと動いてくれたりと、一長一短です。

とはいえ、私のは一番肝心なところはほぼわちゃさんのコードがベースで、
自分でやったのは、結果をクラス化してコードの見通しを良くしたくらいです。

あと、商品(金種)の個数の上限値をあらかじめ決めておくアイデアは、
unibonさんのコードからヒントを得て追加しました。

最小公倍数以上を分離させるのは、動かして試してみたらそれ程効果がなかったので削りました。

まだ細かいバグとかあるかもしれませんので、その点はご承知おきを。

言語はC#です。
コード:
/// <summary>結果</summary>
public class Result {
    private int[] _Prices;
    private int[] _Counts;
    private int _TotalCount;
    private int _TotalPrice;

    /// <summary>コンストラクタ</summary>
    public Result(int[] prices) {
        _Prices = prices;
        _Counts = new int[prices.Length];
        _TotalCount = 0;
        _TotalPrice = 0;
    }

    /// <summary>インデクサ</summary>
    public int this[int index] {
        get {
            return _Counts[index]; 
        }
        set {
            _TotalCount -= _Counts[index];
            _TotalPrice -= _Prices[index] * _Counts[index];
            _Counts[index] = value;
            _TotalCount += value;
            _TotalPrice += _Prices[index] * value;
        }
    }

    /// <summary>商品個数の合計</summary>
    public int TotalCount {
        get { return _TotalCount; }
    }

    /// <summary>価格の合計</summary>
    public int TotalPrice {
        get { return _TotalPrice; }
    }

    /// <summary>自身がtargetより弱い場合trueを返す</summary>
    public bool IsWeaker(Result target) {
        //targetがnullだったらfalse
        if (target == null) return false;
        //合計価格が多い方が強い
        if (target.TotalPrice > this.TotalPrice) return true;
        if (target.TotalPrice < this.TotalPrice) return false;
        //合計商品個数が少ない方が強い
        if (target.TotalCount < this.TotalCount) return true;
        if (target.TotalCount > this.TotalCount) return false;
        //高額商品が多い方が強い
        for (int i = 0; i < _Prices.Length; i++) {
            if (target[i] > this[i]) return true;
            if (target[i] < this[i]) return false;
        }
        return false;
    }

    /// <summary>コピーを返す</summary>
    public Result Clone() {
        Result copy = new Result(_Prices);
        for (int i = 0; i < _Counts.Length; i++) copy[i] = _Counts[i];
        return copy;
    }
}

public class Program {
    private static int[] _Prices;
    private static int[] _MaxCounts;
    private static int _CallCount = 0;
    private static int _CacheCount = 0;
    private static Dictionary<int, Result> _Cache = new Dictionary<int, Result>();

    /// <summary>降順ソート用比較メソッド</summary>
    private static int DescComparison(int x, int y) {
        if (x < y) return 1;
        else if (x > y) return -1;
        return 0;
    }

    /// <summary>商品個数計算</summary>
    private static Result Calculate(int money, int startIndex) {
        _CallCount++;

        //キャッシュに計算済みの結果があったらそれを返す
        if (_Cache.ContainsKey(money * _Prices.Length + startIndex)) {
            _CacheCount++;
            return _Cache[money * _Prices.Length + startIndex].Clone();
        }

        Result bestResult = new Result(_Prices);
        int price = _Prices[startIndex];
        bestResult[startIndex] = money / price;

        if (money % price > 0 && startIndex < _Prices.Length - 1) {
            //ぴったりの場合と、現在の商品が最安の場合は初期値のままでOK
            //そうでなければ高価なものから順に再帰で計算
            for (int i = Math.Min(money / price, _MaxCounts[startIndex]); i >= 0; i--) {
                int testPrice = money - price * i;
                 
                //最適解より自明に弱い場合はループを終了
                if (bestResult.TotalPrice == money
                && testPrice / _Prices[startIndex + 1] + i > bestResult.TotalCount ) break;

                //再帰で計算してみる
                Result testResult = Calculate(testPrice, startIndex + 1);

                //試行結果の方が強かったら置き換える
                testResult[startIndex] = i;
                if (bestResult.IsWeaker(testResult)) {
                    bestResult = testResult;
                }
            }
        }
        _Cache[money * _Prices.Length + startIndex] = bestResult.Clone();
        return bestResult;
    }


    /// <summary>メイン</summary>
    public static void Main(string[] args) {
        try {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            //引数チェック
            int money = 0;
            List<int> priceList = new List<int>();
            bool isArgErr = true;
            if (args.Length >= 2 && int.TryParse(args[0], out money) && money > 0) {
                isArgErr = false;
                Dictionary<int, bool> dic = new Dictionary<int, bool>(); //重複チェック用
                for (int i = 1; i < args.Length; i++) {
                    int price;
                    if (int.TryParse(args[i], out price) && price > 0) {
                        if (!dic.ContainsKey(price)) {
                            priceList.Add(price);
                            dic.Add(price, true);
                        }
                    } else {
                        isArgErr = true;
                        break;
                    }
                }
            }
            if (isArgErr) {
                Console.WriteLine("引数が不正です。");
                Console.WriteLine("{0} 所持金 価格 [...]", Environment.GetCommandLineArgs()[0]);
                return;
            }

            //商品価格の配列をつくる
            priceList.Sort(DescComparison);
            _Prices = priceList.ToArray();

            //各商品の上限を求める
            _MaxCounts = new int[_Prices.Length];
            for (int i = 0; i < _Prices.Length; i++) {
                int max = money / _Prices[i];
                if (max > 0) {
                    for (int j = i - 1; j >= 0; j--) {
                        if (_Prices[j] % _Prices[i] == 0) {
                            //割り切れたら上限を置換
                            max = Math.Min(max, _Prices[j] / _Prices[i] - 1);
                            break;
                        }
                    }
                }
                _MaxCounts[i] = max;
            }


            //再帰による探索
            Result bestResult = Calculate(money, 0);

            sw.Stop();

            //統計データ表示
            Console.WriteLine("キャッシュサイズ :{0}", _Cache.Count);
            Console.WriteLine("キャッシュ使用回数:{0}", _CacheCount);
            Console.WriteLine("再帰呼出回数   :{0}", _CallCount);
            Console.WriteLine("計算時間(秒)  :{0:0.000}", ((Double)sw.ElapsedMilliseconds) / 1000);

            //結果表示
            Console.Write("購入商品一覧   :");
            if (bestResult.TotalCount <= 0) {
                Console.WriteLine("null");
            } else {
                int padLen = 0;
                for (int i = 0; i < _Prices.Length; i++) {
                    if (bestResult[i] > 0) {
                        if (padLen <= 0) padLen = _Prices[i].ToString().Length;
                        else Console.Write("          ");
                        Console.Write("{0}×{1}", _Prices[i].ToString().PadLeft(padLen), bestResult[i]);
                        Console.WriteLine();
                    }
                }
            }
            Console.WriteLine("開始時所持金   :{0}", money);
            Console.WriteLine("購入合計額    :{0}", bestResult.TotalPrice);
            Console.WriteLine("所持金残額    :{0}", money - bestResult.TotalPrice);
            Console.WriteLine("購入合計個数   :{0}", bestResult.TotalCount);
        } catch (Exception ex) {
            Console.WriteLine(ex.ToString());
        }
    }
}


ちょっとだけ脱線しますが、mainを一番下に書くのって、C/C++出身者に多かったりするんでしょうか?
このスレの中でも、一番上に書いてる方と一番下に書いてる方に別れますよね。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-05-17 11:37
引用:

ぼのぼのさんの書き込み (2007-05-17 08:18) より:
ちょっとだけ脱線しますが、mainを一番下に書くのって、C/C++出身者に多かったりするんでしょうか?
このスレの中でも、一番上に書いてる方と一番下に書いてる方に別れますよね。



大昔のCって定義を上に書いておかないといけなかった筈なんで
慣習として各サブルーチンを先に宣言するスタイルが残っているのかも。
とっちゃん
大ベテラン
会議室デビュー日: 2005/07/19
投稿数: 203
投稿日時: 2007-05-17 12:59
引用:

nagiseさんの書き込み (2007-05-17 11:37) より:

大昔のCって定義を上に書いておかないといけなかった筈なんで


本題ネタじゃなくて申し訳ないですが、ここだけ。

C は、呼び出し時点より前に宣言(または定義)がある場合(前方参照)は、その宣言のとおりに、そうではない場合は、int func( ... )と仮定して呼び出します。(現行の Pure C でも通ります。がワーニングでると思いますw)。

C++は、このあいまいな定義を認めていないので、呼び出し時点より前に宣言も定義もなければ、エラーとして扱います。

JavaやC#などでは宣言(はそもそもない)や定義がどこにあってもよい事になっています。

ちなみに、宣言は
int func( int );
という関数の形を宣言したもの
定義は
int func( int a )
{
return a;
}
のように中身もあるものを指します。
#多分、C系言語の用語だと思うので余計ですが一応補足

_________________
// とっちゃん(高萩 俊行)@わんくま同盟
// とっちゃん’Blog
// MS-MVP for Developer Tools - Visual C++
// WindowsInstallerの話題はhttp://www.freeml.com/msiまで

スキルアップ/キャリアアップ(JOB@IT)