|
setup diary |
電力の制御が必要な時には、ソリッドステートリレーまたはサイリスタを使用する。後者としては、これまでサイリスタユニットを購入して利用していた。これは信号の入力にしたがって、出力を制御するユニットです。箱に組み込もうとしたのだが、放熱器は箱の外に出ていた方が良いだろうということで、分解して放熱器以外を箱の中に入れ、放熱器が外にでるようにした。その時にサイリスタユニットの構造をざっと見たのだが、基本的には、制御回路がサイリスタモジュールに適切なタイミングでトリが信号を送るようになっているようだ。
折角なので、サイリスタモジュールの制御の仕方を調べてみた。昔からよく使われている回路では、ダイアックを使ってトリガを作って、これでトライアックなどのサイリスタモジュールを制御する場合が多いようです。最近の回路では、マイコンを使ってトリがを生成する場合もあるらしい。フォトカプラで適切に絶縁しないといけないが、余分な回路が不要になるので、単純化できる気がする。いつかAVRでサイリスタユニットを作ってみようかな。
AVRを使って、サイリスタユニットを作れる気がしたので、いろいろと考えてみた。いくつかの解決すべき問題がある。
まずは、信号の入力をどうするかである。電気的に絶縁した状態で、4-20mAの信号を読み取る必要があるのだが、アナログフォトカプラというものがあるようで、電流の違いを抵抗の変化として検出できるようだ。
次の問題は、AC電源の位相をどのように検出するかである。いくつかの方法があるようだが、トランスで降圧した電圧をブリッジダイオードで正にした電圧を、ツェナーダイオードを通すことによって、ゼロ付近の電圧になったのをデジタル信号としてとらえることができる。ADを持つAVRならば、それを電圧として読み取ることもできる。
最後の問題は、AC電源の正と負をどのようにして判別するかである。トライアックを用いる場合には、これは問題にならないが、サイリスタの場合には正と負のそれぞれで信号を出さなければいけない。センター端子のあるトランスでそれをGNDにして、ダイオードで整流することにすれば、正と負のそれぞれの電圧を別々の信号として受けることができる。
マイコンでこれらの信号を受けて、適切なタイミングでフォトトライアックを介して、トライアックを制御すれば、サイリスタユニットが完成するはずである。
今度、試しに作ってみよう。
validates_acceptance_of validates_associated validates_confirmation_of validates_each validates_exclusion_of validates_format_of validates_inclusion_of validates_length_of validates_numericality_of validates_presence_of validates_size_of validates_uniqueness_of使い方が明らかなものもあるが、複雑そうなものもある。徐々に使い方を覚えていきたい。
本日、メンバーの液晶ディスプレイが故障した。微妙に表示はしているのだが、色が変になって、チラチラするようになった。最近、ディスプレイの故障が多い気がする。暑いからなのかも知れない。ディスプレイが映らないと仕事にならないということで、近くの店で買ってきた。IODATAの23型だが、バックライトがLEDである。まず感じたのは、非常に薄くて軽いということである。これまでの液晶では、バックライトの陰極管のための高電圧発生回路が必要だし、二つの陰極管の光を、面状に分散させるための板なども必要だった。LEDバックライトはどのような仕組みになっているのかはまだ知らないが、面状に発光するために、薄くできるのかも知れない。いつかLEDバックライトの液晶ディスプレイが故障した時に、仕組みを調べてみよう。
void ad_init(){ setbit(ADCSRB,REFS2); // 2.56V setbit(ADMUX,REFS1); // 2.56V clearbit(ADMUX,REFS0); //2.56V clearbit(ADMUX,ADLAR); // bit0-9 setbit(ADCSRB,MUX5); // ADC0-1 clearbit(ADMUX,MUX4); // ADC0-1 clearbit(ADMUX,MUX3); // ADC0-1 clearbit(ADMUX,MUX2); // ADC0-1 clearbit(ADMUX,MUX1); // ADC0-1 setbit(ADMUX,MUX0); // ADC0-1 setbit(ADCSRB,GSEL); // gain 8 setbit(ADCSRA,ADPS2); // CK/128 setbit(ADCSRA,ADPS1); // CK/128 setbit(ADCSRA,ADPS0); // CK/128 setbit(ADCSRA,ADEN); // enable setbit(ADCSRA,ADIE); // interupt enable }ちなみに、ヘッダーでは
#define setbit(PORT,BIT) PORT|=_BV(BIT) #define clearbit(PORT,BIT) PORT&=~_BV(BIT) #define checkbit(PORT,BIT) (PORT&_BV(BIT))と定義しておく。そして、割り込みで結果を受け取る。
ISR(ADC_vect){ unsigned int c; c=ADCL; c|=(unsigned int)ADCH<<8; }最初は1.1Vを基準電圧にして、シングルエンド入力にしていたが、10bitだと1mVの精度しかないことになる。gainを上げようと思ったが、シングルエンド入力だと、gainが1しか選べない。そこで、片側をGNDにつないで、差動入力にしてからgainを8にした。すると、0.3Vが2.4Vになるので、基準電圧を2.56Vにする必要がある。しかし、0.32Vまでしか測れないことになってしまった。理想的には、gainを2で1.1Vを基準電圧にするか、gainが4で基準電圧が2.56Vだと、精度がそれなりに出る。しかし、tiny261のgainは、8,20,32からしか選べない。一方、基準電圧は、1.1Vと2.56VとVccと外部電圧が選べる。Vccは正確な電圧は分からないし、特別な外部電圧を準備するのも面倒だ。もっといろいろなgainが選べれば、うまくいくのだが。
AVRで電圧を読んで、それを計算していたが、その時に浮動小数点を使ってみた。普通にコンパイルは通るのだが、いざAVRに書き込もうとすると、エラーが出る。いろいろと調べてみると、AVRの浮動小数点は、サイズも大きくなるし速度も遅くて、あまり良くないので、固定小数点を使うべきらしい。固定小数点というと難しそうだが、要するに何倍かして、基本的には整数で計算するということだろう。エラーの原因が分かって納得がいった。
ゼロクロスのサイリスタを使っているが、電流計でモニタしてみると、電流のON/OFFの頻度がそれほど高くないようで、電流計の読みがぶれる。基本的には、電源が50Hzだとすると半波を数えると100Hzとなり、出力の大きさにもよるが、ON/OFFのタイミングは、少なくとも10Hz程度はあるはずで、電流計のぶれもそれほど大きくはならないように感じる。オシロで見てみると、一秒間に数回しか切り替えていないように見える。電流が大きくなると、サイリスタはノイズを発生するので、ノイズの少ないゼロクロス方式を使おうと考えているのだが、電流計のぶれが気になる。
気に入らなければ作るしかないかということで、以前計画したAVRを用いてサイリスタユニットを作る計画を実行に移すことにした。石としては、AD変換を使いたいので、tiny261を使うことにする。基本的な回路は以前書いた通りだが、マイコンはトランスで電気的に浮いているので、入力は直接抵抗に流して、その電圧を読むことにした。
電源電圧がゼロになるところは、トランスで電圧を落として、整流して片側だけにした電圧をツェナーダイオードで頭を落としたものを、INT0/ADC9とINT1/ADC2に入力する。そして、falling edgeを割り込みで検出して、そのあとAD変換して、約1V以下になるのを待っている。回路の特性なのか、1V以下になったあとも、なかなか0Vにはならず、だらだらとdecayするので、例えば0.5Vになるのを待っていると、電源電圧のゼロを検出できないようだ。アナログ比較器を使おうかとも思ったが、配線をやり直すのが面倒なので止めにした。
あとは、読み取った出力に応じて、パルスを出す場合と出さない場合を判断するだけだ。しかし、出力がちょうど50%のときには、交流の電圧が正または負の片側だけしか使わないことになってしまうのだが、これは問題ないのだろうか。なんだか気持ち悪い。まあ、そんな偶然はあまり無いから良いか。
とりあえず、汚いけどソースはこんな感じ。
#include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 1000000 #include <util/delay.h> #define setbit(PORT,BIT) PORT|=_BV(BIT) #define clearbit(PORT,BIT) PORT&=~_BV(BIT) #define checkbit(PORT,BIT) (PORT&_BV(BIT)) #define POWER_ZERO 45 // 220ohm*4mA #define POWER_MAX 180 // 220ohm*(20-4)mA static volatile unsigned int power=0; // AD converter void adc_init(){ clearbit(ADMUX,REFS1); // Vcc clearbit(ADMUX,REFS0); // Vcc setbit(ADMUX,ADLAR); // bit3-9 clearbit(ADCSRB,MUX5); // ADC setting clearbit(ADCSRA,ADPS2); // CK/4 setbit(ADCSRA,ADPS1); // CK/4 clearbit(ADCSRA,ADPS0); // CK/4 } int adc_read(){ unsigned char c; setbit(ADCSRA,ADSC); // adc start _delay_us(16); while(checkbit(ADCSRA,ADIF)==0); c=ADCH; setbit(ADCSRA,ADIF); return c; } void pulse(unsigned char c){ setbit(ADCSRA,ADEN); // enable adc while(adc_read()>0x40); setbit(PORTB,c); _delay_ms(1); clearbit(PORTB,c); clearbit(ADCSRA,ADEN); // disable adc } void read_power(){ unsigned int c; setbit(ADCSRA,ADEN); // enable adc ADMUX&=0xf0; ADMUX|=0x00; //ADC0 adc_read(); c=adc_read(); c+=adc_read(); c/=2; //average if(c>POWER_ZERO+POWER_MAX){c=POWER_ZERO+POWER_MAX;} if(c>POWER_ZERO){power+=c-POWER_ZERO;} clearbit(ADCSRA,ADEN); // disable adc } void pin_init(){ clearbit(GIMSK,INT0); /* disable INT0 */ clearbit(GIMSK,INT1); /* disable INT1 */ setbit(MCUCR,ISC01); /* interrupt falling */ clearbit(MCUCR,ISC00); /* 00:L, 10:fall, 11:raise */ setbit(GIMSK,INT0); /* enable INT0 */ setbit(GIMSK,INT1); /* enable INT1 */ } ISR(INT0_vect){ cli(); ADMUX&=0xf0; ADMUX|=0x09; //ADC9 if(power>=POWER_MAX){power-=POWER_MAX;pulse(0);} read_power(); sei(); } ISR(INT1_vect){ cli(); ADMUX&=0xf0; ADMUX|=0x02; //ADC2 if(power>=POWER_MAX){power-=POWER_MAX;pulse(1);} read_power(); sei(); } int main() { cli(); /* disable interuption */ PORTA=0x00; PORTB=0x00; DDRA=0x00; DDRB=0x03; pin_init(); adc_init(); _delay_ms(100); sei(); for(;;); }あとは、パルス出力をフォトサイリスタで受けて、その出力をメインのサイリスタに入れるだけのはずだ。しかし、極性などを間違え無いように気を使わないといけない。
a=1; b=a; b=2; p a #1まあ、これは当然な結果だ。一方、配列を使うと少し違う結果が得られる。
a=[1]; b=a; b[0]=2; p a #[2]配列の代入では、参照が代入されていることが分かる。しかし、別の配列を代入すれば、元と同じような結果が得られる。
a=[1]; b=a; b=[2]; p a #[1]つまり、参照が代入されているので、破壊的メソッドを使うと、元の変数も変化するが、新しく別の値を代入しても、元の変数は変化しないのだ。配列は簡単な計算をするときによく使うので、注意が必要だ。配列の値自身を代入したいときには、
a=[1]; b=a.clone; b[0]=2; p a #[1]とすれば良い。しかし、これも完全ではなく、
a=[[1]]; b=a.clone; b[0][0]=2; p a #[[2]]となってしまう。行列などでは、cloneでは値の複製はできないのだ。ちなみに、rubyのメソッドはすべて値渡しらしい。もっとも、オブジェクトに破壊的なメソッドを使うと、元のオブジェクトも変化してしまうが。何だかややこしい。rubyの変数はすべて参照で、メソッドではその参照が値渡しされるということだろうか。
def fmlwt(fml) awt={} IO::foreach("formula.dat") do |l| awt[$1]=$2.to_f if l=~/([A-Z][a-z]?)\s+([\d\.]*)/ end fw=0 while fml.size>0 break unless m=/([A-Z][a-z]?)([\d\.]*)/.match(fml) aw=awt[m[1]] an=(m[2]=="")?1:m[2].to_f fw+=an*aw fml=m.post_match end fw=1 if fw==0 return fw end原子量のデータを一行ずつ読んで、hashに記録して、その後で化学式を一元素ごとに正規表現でマッチして処理している。しかし、かなり長い気がする。そこで、久しぶりに書き直してみた。まず、書いたのがこれ。
def fmlwt2(fml) awt=Hash[*IO.readlines("formula.dat").map{|l| l.strip.split(/\s+/)}.flatten] fml.split(/(?=[A-Z])/).map{|e| awt[$1].to_f*(($2=="")?1:$2.to_f) if e=~/([A-Z][a-z]?)([\d\.]*)/ }.inject{|s,x| s+x} end原子量のデータは一気に読んで行列にして、一行の前後の空白を除去してから空白で区切って、それをhashに変換している。ここで、コードの節約のために、原子量はまだ文字列のままである。その後で、各元素ごとに区切ってから、正規表現を使って処理して、それを最後にinjectで足している。だんだんゴルフ感覚になってきた。もう少し短くできた。
def fmlwt3(fml) awt=Hash[*IO.read("formula.dat").strip.split(/\s+/m)] fml.split(/(?=[A-Z])/).grep(/([A-Z][a-z]?)([\d\.]*)/){ awt[$1].to_f*(($2=="")?1:$2.to_f) }.inject{|s,x| s+x} endファイルを行をくっつけて読み込んで、それをそのまま配列にしてからhashにしている。後半はgrepを使って正規表現部分をすっきりとさせた。昔書いたコードを見ると、美しく感じられないことが多い。徐々にスクリプトを書く流儀が変化しているからなのだろうが、まだまだ未熟な気がする。しかし、短く書けば良いというものでも無い。後半の処理は良くなったと思うのだが、原子量のhashを作る部分は、最初のやつの方が何をしているのかはっきりしているようにも感じられる。化学式に関して言えば、本当は括弧などにも対応したいのだが、再帰を使えば出来そうな気がしているが、まだ手をつけていない。しかし、こういったプログラムを使うときには、単純な入力ミスが、誤ったデータ処理につながるので、逐一ミスが無いことをチェックすることが重要である。
def fmlwt4(fml) awt=Hash[*IO.read("formula.dat").strip.split(/\s+/m)] r=0 fml.scan(/([A-Z][a-z]?)([\d\.]*)/){|s| s[1]=1 if s[1]=="" r+=awt[s[0]].to_f*s[1].to_f } r end
scanを{|a,n|}で受ければ、s[0]とか書かないでも良くなることに気がついた。
かなり前に書いた、windows用のgpibをrubyから使うためのscriptにバグを発見した。自分のhomepageに載っているものを見ていたら、何か変だと思ったら、バグだった。ibwaitというほとんど使っていないメソッドだったので、これまで気がつかなかったし、それほど大きな問題にならないだろうが、一応訂正しておいた。
このプログラムは、いつ頃書いたのか忘れたが、おそらく2003年ぐらいだったと思う。それまで、計測用のプログラムは、LabView, VisualBasic, IgorProなど、いくつかの言語で書いていたが、いずれも苦痛だった。そこで、rubyで計測用のプログラムを書こうと思い立ち、windowsのrubyからGPIBを使う方法を調べた。ある掲示板には田鎖さんという方が、そんなことをやっていたという形跡は見つかったのだが、よく分からない。それなら自分で書いてしまえということで作ったのです。C言語の使用例などはあったので、Win32APIを使えばあっけなく動きました。Win32APIの使い方もよく理解していなかったのだが。その後、windowsでしばらく計測をしていたが、今ではlinuxに移行したので、私自身はこのscriptはほとんど使わなくなってしまった。その代わりに、田鎖さんのruby-gpibを利用させて頂いている。
ところが、最近の市販の測定システムのほとんどがwindows上で動いている。windowsは、セキュリティー上で問題になるばかりでなく、測定中に再起動しようとしたりしていろいろと面倒なのだが、そのような流れになってしまっている。このような装置を自分でGPIBから制御するときには、windows用のGPIBが役に立つ。
さて、linux上で測定をしている人はwindowsほどは多くないようで、なかなか情報交換の機会が無い。そのため、それぞれ独自の手法を取ることが多いように感じられる。私は、ruby+ruby-gpib+Tkで測定をしている。Tkはくせがあってあまり好きではないが、GUIを作る時にはある程度の苦痛は我慢するしかないだろう。
def bracket(fml) while fml=~/^(.*)\(([^\)]*)\)([\d\.]*)(.*)$/ head=$1 inside=$2 number=$3.to_f tail=$4 inside.scan((/([A-Z][a-z]?)([\d\.]*)/)){|s| s[1]=1 if s[1]=="" head+=s[0]+(s[1].to_f*number).to_s } fml=head+tail end fml end括弧の中を、その後に続く数倍して、括弧を取り除くことができる。これを式量計算のスクリプトで処理すれば、括弧付きの化学式でも、式量を計算できることになる。
必要な小型の部品が必要なときに、ちっちゃなNCフライスを使っている。最近は酷使しているので、いつか壊れないか心配している。しかし、市販のものはそれなりに高価である。しかし、ミニフライスにステッピングモーターを取り付けて、それをPCから制御するようなCNCフライスなら、結構安く手に入るらしい。
そこで、いろいろと調べてみたら、専用のソフトを使ってPCのパラレルポートでパルスを作り、それをモータのドライバーに入力している。パラレルポートは、今後姿を消す可能性が高いし、PCでパルスを作っていると、別のタスクが走ると、速度が変化する可能性がある。PCは1ms以下の時計を持っていないので、それ以下の時間制御は苦手で、おそらく空のループを回して時間を制御しているのだろう。そう考えると、PCから座標などのデータを送って、マイコンでパルスを作るようにした方が理想的である。というわけで、AVRでNCを制御する時の問題点を考えてみた。
まずx軸とy軸の同時移動である。直線的に動くためには、x軸とy軸にパルスをどのようなタイミングで送るかを決めなければならない。これは以下のようにすれば良い。それぞれの軸方向にdx,dyだけ動かすとして、dx>=dyとする。このとき、常にx軸にはパルスを送る。x軸にパルスを送るときに、変数にdyを加算し、もしそれがdxの値以上だったら同時にy軸にパルスを送り、dxを減算するということを繰り返す。すると、x軸にdx回パルスを送ったときに、y軸にdy回のパルスを送ることができる。
次に問題なのが、加速と減速時のパルス間隔をどのように決めるかである。加速度を指定したときには、どのようにパルス間隔を変化させれば良いかを、適当に数式をいじって導いた。加速度をa、一つのパルスで動く距離をdとすると、最初のパルス間隔をw0は、sqrt(d/q)となる。現在のパルス間隔w(i)に対して、w(i+1)/w(i)=w0**2/(w(i)**2+w0**2)とすれば良いようだということが分かった。このパルス間隔の割合の数列をあらかじめAVRのFlashに記録しておき、それを用いてパルス間隔を変化させていけば良いだろう。
まだ検討中の課題としては、バックラッシュをどのように処理するかとか、円弧を描くときにはどうするか、などがある。
AVRとCNCで検索をかけると、AVR-CNCというのがすでにあるらしい。同じようなことを考える人はいるものだ。不完全な日本語のページもあったが、機械翻訳がひどかったので、日本語訳を投稿しておいた。いつか採用されるかな。AVR-CNCに関しては、いつかdownloadして、どうゆうものか調べてみよう。
AVR-CNCを読んでみた。まず、PC側のソフトはWindows用だった。AVR側では、加速などは行わず、いきなり特定の速度で軸を動かすようになっていた。直線補間は、先に書いたようなことをx,y,zの三つの軸で行っている。さらに円弧補間もあったが、math.hを使っていたので、tinyでは難しいだろう。バックラッシュの処理はおそらくPC側で行っているのか、AVRのソースには見当たらなかった。
久しぶりにパソコン工房のhomepageを見ていたら、「ちょいパソ」というものを見つけた。ARM9を搭載して、600gで一万五千円程度である。ディスプレイは七インチで800*480とまあまあである。また、USBポートもある。非常に魅力的に感じたか、致命的なのがOSで、WindowsCEである。これをlinuxかBSDで動かすことができれば、買いなのだが、少し検索したところ、まだ情報はあまり無いようである。いずれはだれかがやってくれるだろうが、それまでは待ちかな。
以前、mobile gearIIにNetBSDを入れてしばらく使っていたが、emacsなどを立ち上げると、かなり遅く感じた。最近はめっきり使っていないが、まだ可愛いので捨てるのを躊躇している。sharpのNetWalkerZ1が出たときには、linuxだったので、良かったのだが、キーボードの評判が良く無かったので、見送った。しかし、次に出たT1では、キーボードが改良されるのではなく、無くなってしまったので、購入候補からは完全に外れてしまった。ちょいパソの今後に期待できると良いのだが。
グラフを書くときに使っているRだが、プログラム言語としてもそれなりに使える。統計的な計算を含んだ簡単なプログラムの場合には、別の言語でその統計処理のルーチンを書くよりも、Rに元からある関数を使う方が早い場合がある。
しかし、Rの目的は通常のプログラムを作ることではないので、他の言語では簡単にできることが、Rではどのようにしたら良いのか分からない場合もある。その一つがファイルからのデータの読み込みである。data frameにデータを取り込む場合には、read.tableを使ってやっているが、通常の文章などのファイルを読み込む方法がこれまで分からなかった。もしあるとすれば、readなんとかという関数だろうと思って、help.search("read")としてみると、readLinesというのがあって、ファイルの内容を行単位で読み取ることができる関数を見つけた。Rではもっといろいろなことができるのだろうが、良いリファレンスマニュアルは無いのかな。
Rubyではこのmethodを使えば良いということが分かっても、それと同じことをするRの関数が分からないとプログラムを書くのに四苦八苦してしまう。試しに、文字列操作の関数のRubyとRの対応表をつくってみよう。
R | Ruby |
paste(x,y,sep="") | x+y or a.join("") |
substr(x,start,stop) | x[start..stop] |
sub(pattern,replacement,x) | x.sub(pattern,replacement) |
gsub(pattern,replacement,x) | x.gsub(pattern,replacement) |
strsplit(x,split) | x.split(split) |
rubyのa.join("")に対応するのは、Rのpaste(a,collapse="")かもしれない。
本日、ある人から、液晶の割れたディスプレイを頂いた。これで、手元にある故障している液晶ディスプレイが三台となった。通常、壊れたものが三つあれば、うまく組み合わせれば、二つぐらいはうまく動くものが作れる。というわけで、部品を入れ替えて、ディスプレイの復活を試みた。
割れた液晶パネルを、別の液晶パネルと入れ替えれば、とりあえず一台は復活するはずである。しかし、そう単純では無かった。一台はbenq、一台はSharpで、もう一つはIODATAと、三つともかなり違うメーカーのものだった。液晶パネルの大きさが微妙に違うのである。どれも同じ19インチだったので、共通の規格だろうと思っていたら、縦横と厚さも違っていて、うまくいかない。それならばと、液晶パネルを分解して、最前面の液晶板だけを入れ替えようとしたが、これも厚さの違いによって挫折。最終的には液晶パネルと筐体の間にできた隙間にスペーサーをつめて、強引に組み立てた。なんとか一台は復活したが、納得がいかないやり方になってしまった。
最後の一台の部品も流用できないかも検討したのだが、これはまた規格が違っていた。通常は上部にあるコネクタが下部にあり、さらに陰極管のコネクタも他のものとは違っている。互換性という意味では、他のパネルとは入れ替えることができない。
三台から二台を復活させるつもりだったが、中途半端な一台だけしか復活させることができなかった。微妙に敗退感がある。
Rでグラフを書きながら、測定の様子を観察していると、待っているときに、Rで楽にグラフが書けるようにRをいろいろといじってしまう。言語としてのRは、それなりに興味深い仕様になっている。
関数を定義すると、その中で定義された変数はローカルになる。しかし、外の変数を読むこともできるので、その変数がどの変数なのかが分からなくなってしまう。変数をすべてグローバルにしてしまえば、ややこしくはなくなるが、どうしても変数名が重複する可能性が出てくるので、変数はローカルであるべきだ。
次のような例を考えてみよう。
x<-1 test<-function(){ cat(x) x<-2 cat(x) } test() cat(x)
この実行結果は121となる。testという関数が呼ばれて、最初にxを見ると、関数の外で定義されたxの値となり、関数の中で代入するとその値になる。そして関数の外で見ると、元の値になっている。つまり、関数の中の一個目と二個目のxは別のxになっているのである。
こうなってくると、関数から外の変数の値が見えるのが、便利なのか不便なのかが分からなくなってくる。
このような場合には、関数内だけの環境を作って、その環境を指定して変数に代入したり呼び出したりすればよい。長くなるけど、こんな感じになる。
x<-1 test<-function(){ cat(x) testenv<-new.env() assign("x",2,env=testenv) cat(get("x",env=testenv)) } test() cat(x)これで、xの意味合いがはっきりとする。しかもtempenvは関数の中のローカル変数なので、外からは見えない。でも、面倒だな。
Rはグラフソフトとして使っていて、これまで言語としてとらえていなかった。また、一貫して勉強したことが無いので、その言語の特徴をいまいち理解していない。しかし、くせはあって組みにくいけど、なかなかおもしろい言語のように思えてきたので、少しずつ勉強していけたらと考えている。
Rには様々なオブジェクトがあるが、どれだけの種類があるのだろう。オブジェクトの種類は、typeof()で調べることができる。
Rのオブジェクトにベクトルがあるが、これにはさらに五つの型があって、異なる型のデータを入れることはできない。つまり、c(1,"2")とはできない。実は、スカラーもベクトルと考えているようだ。'1'[1]としたら"1"が返ってきたし、"1"==c("1")はTRUEとなる。
異なる型を配列にできるオブジェクトとしては、リストがある。また、それぞれの要素には成分名をつけることができるので、連想配列のように使うこともできる。例えば、d<-list(a=1,b=2)としておけば、d[[1]]でも良いが、d$aでもアクセスできる。ファイルからデータを読み込むときにつくるデータフレームにアクセスの仕方が似ていると思って、試しにデータフレームをtypeof()で調べてみたら、listとなった。つまり、データフレームはリストだったようだ。しかし、d<-list(a=c(1,2),b=3)とすると、d$bは当然3になるが、d<-data.frame(a=c(1,2),b=3)とすると、d$bはc(3,3)となる。厳密には多少違うようだ。
まだまだ勉強する必要がありそうだ。
d<-list(1, 2, c(1, 2, 3)) deparse(d)とすると、そのまま"list(1, 2, c(1, 2, 3))"となる。つまり、この頭に"d<-"をつければ、そのオブジェクトを作るコードが保存できることになる。なるほど。
deparse()をつかってプログラムを書いていたら、なんだか思ったように動かない。原因を調べていたら、長いデータをdeparse()すると、ベクトルになって返ってくることが原因だった。Rでは、ベクトルの処理は独特である。例えば、c(1,2,3,4)+c(4,5)は、5 7 7 9となり、c(1,2,3,4)+c(4,5,4,5)と解釈される。このような挙動のために、変になっていた。paste(deparse(),collapse="\n")で、一つの文字列にすると、問題なく動いた。