- ベストアンサー
Jasminを使用して10桁のフィボナッチ数列を表すには・・・??
初投稿です、よろしくお願いします。 プログラミング初心者で、まだ右も左もよく分からないヒヨッコですが、 この間学校でjasminコードというものを習い始め、まだちゃんと理解していないうちに、こんな問題を出されました。 ================================ 以下のコードをjasminで表しなさい。 public class exam2 { public static void main(String args[]) { dofib(9); } public static void dofib(int n) { int n1 = 0; int n2 = 1; int tmp; if ((n < 0) || (n == 0)) System.out.println("0"); for (int i = 0; i < n; i++) { System.out.println(n1 + " "); tmp = n2; n2 = n1 + n2; n1 = tmp; } } } フィボナッチ数列を10桁まで表示するコードです。 ================================ 私と友達が作ってみたjasminコードはこちらです。 .class public examples/Fibo .super java/lang/Object ; ; standard initializer .method public <init>()V aload_0 invokenonvirtual java/lang/Object/<init>()V return .end method .method public static main([Ljava/lang/String;)V .limit locals 5 .limit stack 10 getstatic java/lang/System/out Ljava/io/PrintStream; astore_1 bipush 10 istore_2 iconst_0 dup ;invokevirtual java/io/PrintStream/println(I)V; ;invokestatic java/lang/String/valueOf(C)Ljava/lang/String; invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V istore_3 bipush 1 istore 4 L0: iload 3 dup iload 4 dup ;invokestatic java/lang/String/valueOf(C)Ljava/lang/String; invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V iadd iload 4 istore 3 istore 4 iload_2 ifeq L1 iinc 2 -1 goto L0 L1: return .end method しかし、これではうまく動作しませんでした。 何か解決方法や提案など、ございましたら教えていただけたら幸いです。 どうぞよろしくお願いします。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
先生の意図も質問文のjasminのコードも(どの行がどの行に対応するのかも)さっぱりだ。 一行一行丁寧に訳す。 心残り: 1.ラベルを大量に使っちゃったこと 2.ループ変数のスコープがループではなくメソッドになってしまっている 3.前方が真だったとき,後者は実行しないかもしれないけど, 書いたコードでは両方実行しちゃっていること 4.スタックサイズと変数の数の見積もりがいい加減なこと #初めて触った言語。new instructionには戻り値がなく,さらにコンストラクタを別に呼び出さないといけないということが分からず一時間くらい迷走したが,一応コード完成。 #なお,このコードは逆アセンブルしたものとは異なるだろう #WikipediaのJava仮想マシンのオペコードの説明が参考になった。 メモ: bipush等はbyte型で,高速らしいが,混乱するのでint型に集中できるようldcを使った。 iload_0等はよく使うから用意してあるらしいが iload_3までしかなく,iload_4とかやろうとしてエラーに見舞われたので iload 0 とちゃんと分けて書くことにした。 混乱したので, 自分は割り切って istore (スタックから取り出して,その番号の領域に入れる) iload (指定した番号スタックに積む) しか使わないことに決めた。 #↓念のためスタックの図。先は読まなくていい #http://www.cc.kyoto-su.ac.jp/~yamada/ap/stack.html #型によってiload fload,dload,aloadを使い分ける(順にint,float,double,それ以外) インスタンスメソッドを使うには インスタンスをスタックに積んでおいて invokevirtual メソッド名(引数の型)戻り値 スタティックなメソッドを実行する時は invokestatic メソッド名(引数の型)戻り値 java.lang.System.outはSystemクラスのstaticなメソッドでjava.io.PrintStreamのインスタンスを返す。 printlnはjava.io.PrintStreamクラスのインスタンスメソッド その後引数に必要なものを積む。 メソッドを使う直前に使うものを全てのものを引き出すことを意識する。 メソッドやisub,iaddを使った後は… インスタンス,引数に渡した物はそれぞれ消えて,戻り値がスタックに積まれる。 その引数をすぐに使わない場合はstoreしておく。 どうしても混乱する場合は後で使うことを前提に変数に入れてしまおう。 System.outのようにどこかのstaticなメソッドを実行するのではなく インスタンスを生成する場合は new クラス dup invokespecial コンストラクタ を行うことでスタックに積まれるようだ。 変数に入れる必要がないのにスタックに余ってしまった物はpopする。 .class public exam6 .super java/lang/Object ; standard initializer .method public <init>()V aload 0 invokenonvirtual java/lang/Object/<init>()V return .end method ; public static void dofib(int) .method public static dofib(I)V ; set limits used by this method .limit locals 15 .limit stack 15 ;int0:n ;int1:n1 ;int2:n2 ;int3:tmp ;int4:i(loop variable,but I don't know how to declare only for the loop) ;n1 = 0 ldc 0 istore 1 ;n2 = 1 ldc 1 istore 2 ; leftexpression or right expression ;left expression ;n < 3 → n - 3 < 0 ;n < 0 → n - 0 < 0 ;n - 0 iload 0 ldc 0 isub ;load 1(true) or 0(false) onto stack,so that I can use it with ior later. ;after that, goto MIXAFTER1 ifle MIXLOGOP1 goto MIXLOGOP2 MIXLOGOP1: ldc 1 goto MIXAFTER1 MIXLOGOP2: ldc 0 goto MIXAFTER1 MIXAFTER1: ;right expression ;n - 0 < 0 iload 0 ldc 0 isub ;load 1(true) or 0(false) onto stack,so that I can use it with ior later. ;after that, goto MIXAFTER2 ifeq MIXLOGOP3 goto MIXLOGOP4 MIXLOGOP3: ldc 1 goto MIXAFTER2 MIXLOGOP4: ldc 0 goto MIXAFTER2 MIXAFTER2: ior ifne IFLABEL1 goto IFLABEL2 IFLABEL1: ;I think it's better to write in the original Java Source Code ;getstatic java/lang/System/out Ljava/io/PrintStream; ;ldc 0 ;invokevirtual java/io/PrintStream/println(I)V getstatic java/lang/System/out Ljava/io/PrintStream; ldc "0" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V ;may be the original Java source lacks "return" here. IFLABEL2: ;i = 0 ldc 0 istore 4 ;start loop WHLABEL1: ;check for the loop constraints ;i < n ;i - n < 0 iload 4 iload 0 isub iflt WHINNER1 goto WHAFTER1 WHINNER1: ;I think it's better to write in the original Java Source Code ;getstatic java/lang/System/out Ljava/io/PrintStream; ;iload 1 ;invokevirtual java/io/PrintStream/println(I)V getstatic java/lang/System/out Ljava/io/PrintStream; new java/lang/StringBuffer dup invokespecial java/lang/StringBuffer/<init>()V iload 1 invokestatic java/lang/Integer/toString(I)Ljava/lang/String; invokevirtual java/lang/StringBuffer/append(Ljava/lang/String;)Ljava/lang/StringBuffer; ldc " " invokevirtual java/lang/StringBuffer/append(Ljava/lang/String;)Ljava/lang/StringBuffer; invokevirtual java/lang/StringBuffer/toString()Ljava/lang/String; invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V ;tmp = n2 iload 2 istore 3 ;n2 = n1 + n2; iload 1 iload 2 iadd istore 2 ;n1 = tmp; iload 3 istore 1 ;i++ iload 4 ldc 1 iadd istore 4 goto WHLABEL1 WHAFTER1: return .end method .method public static main([Ljava/lang/String;)V .limit stack 5 .limit locals 100 ldc 9 invokestatic exam6/dofib(I)V return .end method
その他の回答 (3)
- Tacosan
- ベストアンサー率23% (3656/15482)
心残り2 については, JVM では不可能だったはずです>#3. あと細かいところでは ・new は「新しく作ったオブジェクト」をスタックに残します. だから, dup+コンストラクタ呼び出しで初期化できます. ・for や while は「条件判断をループ本体のあとに書く」ようにコンパイルすることになっているので, そのコードは逆アセンブルしたものと確実に異なるはずです.
- Tacosan
- ベストアンサー率23% (3656/15482)
う~ん, 方針がよくわからんなぁ. 「Java を習っていない」=「Java を知らない」ことを前提にしてしまうと, 「以下のコードをjasminで表しなさい」という問題そのものが成り立たないんだが.... なお, 「うまく動作しない」というときには「どのような動作を期待したか」「実際にはどのような動作であったか」を書くようにしてください. また, 何かメッセージが出力される場合にはそれもつけてください. しかし謎のプログラムだなぁ. 例えば getstatic java/lang/System/out Ljava/io/PrintStream; astore_1 ってやってるけど, その後この変数は 1回も使ってないよね.
お礼
続いてのご返信、どうもありがとうございます。 先生の方針は私も理解しかねています。しかしC#を他クラスで学んでいるので、『このぐらいのコードは分かるだろう。』と先生は考えているのでは、と自分を納得させています。 申し訳ありません、説明不足でしたね。期待する動作は 0 1 1 2 3 5 8 13 21 34 とjasminのファイルを実行した時に表示されることです。 今のところ現れるエラーメッセージは C:\Users\YDI\Desktop\jasmin\jasmin>java -cp . examples/Fibo Exception in thread "main" java.lang.VerifyError: (class: examples/Fibo, method: main signature: ([Ljava/lang/String;)V) Expecting to find object/array on stack Could not find the main class: examples/Fibo. Program will exit. 謎のプログラム、その通りだと思います。jasminをついこの間知ったばかりで、先生に教えてもらった数例(forループをjasminで表す等)を参考にしながら手探り状態で書いたものなので極めて稚拙なコードになっているのでしょう。本当に初心者で申し訳ありません。いくつかサイトを見てみたのですが、jasminコードがしっかり日本語で説明されているサイトがなかなか見つからず、今回質問させていただいた次第です。 getstatic java/lang/System/out Ljava/io/PrintStream; astore_1 は astore_1に java/lang/System/out Ljava/io/PrintStream というコードが入れられていると思っていました・・・。
- Tacosan
- ベストアンサー率23% (3656/15482)
「一度 Java で書いてから逆アセンブルする」のはダメなんだろうか.
お礼
Tacosanさん さっそくのご回答、どうもありがとうございます。 逆アセンブルなんて方法があったとは!!知りませんでした。 ただ、『javaを学ぶ前にjasminを学ぶ』という先生の方針なので、 まだjavaを習っていないのです。 C#ならば他の授業で習っているので分かるのですが・・・。 ただ、後々に使えそうな知識ですね! ご提案、どうもありがとうございました。
お礼
>>himajin100000さん ご返信、どうもありがとうございます。 一行一行丁寧に訳す。了解しました。やはり全てのコードについて、 なぜこうなるのか?が分からなければダメですね。元あるコードを繋いで、なんとなくプログラムを組むのではなく、しっかり意味を追って1行1行を理解しなければ何の意味もありませんよね。 それと、貴重な回答例どうもありがとうございます!時間も大分かけさせてしまったみたいで、ご迷惑をおかけしました・・・。 メモやスタックの図など、参考にしながらコードを読み解きたいと思います。自分の滅茶苦茶なコードもどうにかしなければなりませんし。