ラベル return の投稿を表示しています。 すべての投稿を表示
ラベル return の投稿を表示しています。 すべての投稿を表示

詰めSAS12回目:数字が1文字ずつに分解された、数列から100番目の値を取得する

リンクさせていただいたサイト「うずまき (3rd Gen)」の管理人じゅんさんに、あるプログラマ向けチャレンジサイトの問題を教えて貰いました。

ただ、何のサイトか書いて、そのまま問題文載せて、解答例を示してしまうと、ちょっとまずいかなと思うので、問題を参考にした上で内容を変更しています。結構ありがちで一般的な問題だと思いますが。

そもそもSASで解く人なんて皆無だから問題ないと思いますけど、、。

問題:
4,6,8,1,0,1,2,1,4,.......と続く数列のN番目の値について、Nをマクロ変数で与えて実行すれば、値が表示されるようなSASコードをかけ。そして、取り敢えずN=100のときの値を示せ

あ~!こういうの見るとダメですよね。将棋好きに詰め将棋みせるのと同じで、どれだけ忙しくても考えることを避けれない。
ようするに4から+2ずつしていくけど、値が2桁以上になったら、1桁ずつ分解されるっていうルールの数列ですね。

僕が書いたのは以下の感じです。

%let N=100;

data _NULL_;
file print;
retain  K 0;
do i=1 to &N;
 X=put(2+(2*i),best. -L);
 do j=1 to length(X);
  Y=char(X,j);
  K+1;
  if K=&N then do;
    put Y;
    return;
  end;
 end;
end;
run;

答えは






[1]です。




なにをしているかをわかりやすくするため、

%let N=10;

data A1;
file print;
retain  K 0;
do i=1 to &N;
 X=put(2+(2*i),best. -L);
 do j=1 to length(X);
  Y=char(X,j);
  K+1;
  output;
  if K=&N then do;
    return;
  end;
 end;
end;
run;

とすると


















Kが実際何番目かのカウンタです。Xが加算ででてくる値で、それを1桁ずつ分解したのはYです。
Kが設定したNになった時点でループを打ち切ります。

さて、じゅんさんが提示されたコード(問題を変えたことにより、影響のあった箇所等のみかえています。)は

%let x=100;
data A2;
 length line2 $ 32767;
 DO num=6 TO 4+&x*2 BY 2;
  numvar=num;
  line=PUT(numvar,best12.);
   IF num=6 THEN DO;
    line2='4'||line;
    line2=COMPRESS(line2,' ');
   END;
   IF num NE 6 THEN DO;
    line2=TRIM(line2)||TRIM(line);
    line2=COMPRESS(line2,' ');
   END;
  END;
  ans=SUBSTR(line2,"&x",1);
 run;

 PROC PRINT data=A2;
  var ans;
 run;

で、結果は同じです。

なるほど、オブザベーション起こすわけではなく、1変数内で横に連結していくわけですね。問題のイメージと処理のイメージが合致しています。1変数内の文字値制限を超えない限りは、こちらの方が自然かもしれません。


さて、多分、同じ思いの方が多いと思いますが、この問題、めっちゃIML向きですよね。
というかSAS向きでないのか?

一応書いてみたのが

%let N=100;
proc iml;
X=compress(rowcat(char(shape(t((2:&N+1)*2),1))));
Y=substr(X,&N,1);
print X Y;
quit;



処理過程がわかりやすいようにX Y二つに分けていますが、答えはYです。

ただ、多分X=の部分もっと短くかけると思うのですが、まだIMLに慣れていなくてわかりませんでした。どなたか教えてください。


筋悪ですが、LINKステートメントとGOTOステートメントについて

SAS Technical News 2010 Autumn 「意外と知らない!? コロンの活用術をご紹介」は僕のお気に入りの号のひとつです。

http://www.sas.com/offices/asiapacific/japan/periodicals/technews/index.html#section=4

SASにおけるコロン「:」はトリッキーかつ結構いい働きをするやつで、これをうまく使えるかどうかで、コードの完成度に差がでると思っています。僕の中では桂馬みたいなイメージです。

で、だいたい上記の号の中でコロンの使いどころについて言及されているのですが、ちょっと勝手に感じたのが、やっぱりSAS的にはLINKステートメントやGOTOステートメントにおけるラベルコロンは紹介すべきではないと考えているのかなということでした。

LINKやGOTOステートメントは、プログラムの途中で指定箇所(ラベルといわれる)にぶっ飛ぶことができる危ない奴です。

GOTO文は筋悪というのは、多くのプログラム言語に当てはまることで、SASにとっても
上から下に流れるSASのシンプルな処理をぶち壊す、やっかいなステートメントです。

恐らく、ほぼ全てのケースにおいてGOTOやLINKを使わずに、目的の処理が果たせるコードはかけるはずなので、使用はお勧めしません。

ただ、コードを書く人も、読むであろう人も全員、熟練者であれば、相当複雑なネストを組む時に、全体をすっきりさせるために使用することはあるのかもしれません。

しかし、もしかしたら、運悪く、どこかで他人の書いたGOTO や LINKのコードに対峙することがあるかもしれません。そんな時の対抗手段として、基本的な挙動は知っておいて損はないと思います。

例えば以下のようなデータセットがあったとします。

data Q1;
do X=1 to 5;
 do Y=2 to 4;
  output;
 end;
end;
run;



















これに対して以下のコードを実行すれば

data A1;
 set Q1;
 by X;
 if first.X then Z=0;
  Z+Y;
run;



















となります。


これをLINKステートメント使って表現すると

data A2;
 set Q1;
 by X;
 if first.X then link FL;
  Z+Y;
 return;

 FL:
  Z=0;
 return;
run;

となりますFLというのがジャンプ先の目印になって
そこからジャンプ先での処理が行われて、returnで戻ってきます。

ちなみにlinkの部分をgotoにすると

data A3;
 set Q1;
 by X;
 if first.X then goto FL;
  Z+Y;
 return;

 FL:
  Z=0;
 return;
run;

returnで停止するので



















僕が知っているのも、ほんとこの程度までです。