#1. GASとC言語等との違いとクラス(簡単に)
- 型など,人間側で厳密に書かなくても自動判定できるところは勝手にやってくれる印象。
- 変数のスコープ(変数が有効な範囲)はCと同じと考えてほぼ間違いない。
- リマーク分は,//,や,/**/が使える。
- 変数は型推定が行われるのですべて,varとして宣言する。
- プリミティブ(原始的)な型は,newを使ってインスタンス(オブジェクト)を生成する必要はない。具体的なプリミティブな型は整数・実数・文字・文字列など。インスタンス・オブジェクトは後述。
- 代入するもので型が決まったり型が自動変換されるなどする。
変数の宣言例
var num = 0;
var str = "abc";
for(var i=0; i<10; i++){
・・・
}
- 配列の初期化は大括弧[]でくくる。
- for文は通常の使い方の他,配列の要素数を気にしない拡張for文が使える。
拡張for文
var strings = ["Good Morning\n", "Good Afternoon\n", "Good Evening\n"];
for(var index in strings){
strings[index]を表示するなどのプログラム
}
- 次のようなnewを使った配列の作り方もある。
配列
var strings = new array("Good Morning\n", "Good Afternoon\n", "Good Evening\n");
for(var i in strings){
strings[i]を表示するなどのプログラム
}
- 文字列・文字を表現するのに,ダブルクォーテーション,シングルクォーテーションのどちらで囲っても良い。一文字の時シングル,文字列の場合ダブル,というように決めておくと良い。
- 配列の場合は,stringsの様に変数名に「s」を付けるというルールが一般的。
- 変数名・関数名は小文字で始めて単語の切り替わりは大文字にするルールが一般的。例えば,googleAppsScriptという感じに大文字を入れる。
- GAS(JAVA, C#, C++等の言語)では変数はインスタンスやオブジェクトと呼ばれる。プリミティブな場合は,単に,変数,とか,プリミティブな変数,と言われる。
- クラス名の表記は一般的に最初の文字が大文字。例えば,Documentとか,DocumentForDebugなど。
- GASプログラミングで出てくる,クラス,インスタンス,オブジェクト,メンバ変数,メソッド,thisの意味は次の通り。
クラス等の言葉の意味
クラス ・・・設計図,例えばテレビの設計図。
インスタンス・・・実体。設計図に従って生産された実際のテレビ。何台でも生産できる。生産(生成)には演算子newを使う。
例:var tv = new Television;//Terevisionという設計図から新しくテレビを作成。tvがインスタンス。
もちろんTelevisionという設計図はプログラム中のどこかに書いておく(定義しておく)。
オブジェクト・・・物体。インスタンスと同じと考えて良い。
テレビは物体であり,Televisionという設計図の実体。
つまり,Televisionクラス(型)のインスタンス(実体)tvは,オブジェクト(物体)の一つである。
Televisionクラスのオブジェクト,Tevevitionクラスのインスタンス,と表現してもおかしくない。
インスタンスもオブジェクトもニュアンスの違いしかないので,同じと考えて良い。
メンバ変数・・・構造体のメンバ変数と同じ。
Televitionクラスのインスタンスとしてtv1,tv2があったとして,映っている番組が違う場合,
各インスタンス内のchannelというメンバ変数の値が異なると考えて良い。
メソッド ・・・構造体の中の関数。C言語では構造体の中に関数は書けないが,クラスではメソッドと呼ばれる
メンバ関数が記述できる。例えば,channnelというメンバ変数の値によって,
画面に映像を映すメソッドdisplay()。
this ・・・クラス(設計図)の中で,やがて生成されるであろうインスタンスを指す。
this.channelと書けば,「そのインスタンスtvのchannel」という意味。次の章を参照。
参考:http://marupeke296.com/IKDADV_JAVAS_Class.html
- プリミティブ(原始的)でない型,つまりクラスについては,自分で定義する場合もあるが,たくさん準備されているのでそれを利用する機会が大半。オブジェクトの生成は,new演算子を使う。次の例ではすでにシステム側で用意されているDateクラスのオブジェクトnowを作っている。Dateクラスは筆者が定義を書いた(つまり,プログラミングで記述した)わけではない。
オブジェクト(インスタンス)の生成
var now = new Date(); //現在時間を取得,シリアル値で得られる
var str = now.toString(); //シリアル値を文字列に変換(例:'Sat Oct 29 2016 16:05:42 GMT+0900 (JST)')
var strings = str.split(' '); //半角スペースで切り分けて配列に入れる, splitはメンバ関数(メソッド)
ここでstrings[4]を表示するプログラムを書くと,時間が表示される。
- すでにファイルとして存在するスプレッドシートやドキュメント等はすでに存在するオブジェクトなので,それを扱うのにnewはしない。それらを新規作成する場合もnewでなく,専用の関数(メソッド)がある。
- ファイルは固有のIDで管理される。従って同じファイル名も存在できる。ややこしいので注意する。
- ファイルIDはファイルをブラウザ上で開いて,URLで確認する。
- フォルダIDも同様に,そのフォルダを開いて,URLで確認する。
- 関数はすべて,次のように定義。returnは必要に応じて書く。返すものがなければ書かなくてよい。型は自動判定されるので関数に型指定は不要。可変引数(ここでは取り扱わない)も可能。
関数の定義の方法
function funcName(argument1, argument2, ・・・){
・・・
return ・・・;
}
- Cの構造体をGASではクラスと呼ぶ。クラスにはメンバ変数の他,メンバ関数がある。それらはインスタンスに「.」でつなげて指定する。例えば次の
toString()
やsplit(' ')
がメンバ関数である。
インスタンスのメンバ関数の呼び出し方
var now = new Date(); //現在時間を取得,シリアル値で得られる
var str = now.toString(); //シリアル値を文字列に変換(例:'Sat Oct 29 2016 16:05:42 GMT+0900 (JST)')
var strings = str.split(' '); //半角スペースで切り分けて配列に入れる
ここでstrings[4]を表示するプログラムを書くと,時間が表示される。
- メンバ関数は,メソッドと呼ばれる。どんなメソッドがあるのかは,マニュアルやネット情報によるしかない。
- 文字列などプリミティブ(原始的)な型も実はメソッドを持つ。例えば次の例のsplit。
プリミティブな型のメンバ関数
var str = "Good Morning";
var words = str.split(' ');//半角スペースで分離。splitはメンバ関数。
ここにwords[1]を表示するプログラムを書くと,Morning が表示される。
- 文字列の連結は,単に「+」でつないでいけばよい。strcatの様な関数は使わない。連結するデータが入った変数が数値でもうまく勝手に変換してくれる。itoaの様な関数は使わない。
- この本を読めばいい,という王道はない。ひたすらネット検索でメソッド等の情報を集めたり,誰かが書いたサンプルプログラムを研究するしかない。
- GASのマニュアル:https://developers.google.com/apps-script/
#2. プログラムのデバックのためlogファイルを作る
##2.1 クラスを使わない例
- デバッグやエラーを出力するlogファイルを作成。新規,から,Googleドキュメント,を選択し,ドキュメントを新規作成する。
- 名前を「log」に変更する。勝手に保存される。
- ドライブを確認すると,logができている。
- logをダブルクリックすると開く。(閉じた場合は開いておく)
- logに実行ログを書き込んだり,エラーを書き込んだりするので,開発中はlogを開いたままにしておくとよい。
- GASプログラムはフロッピーマークをクリックして自分で保存する。保存し忘れても次回起動時に保存されてないものを復活させるかどうか聞かれるので慌てる必要はない。
- logのファイルIDをコピーし,あとで使用するため,エディタ等に貼り付けておく。
- Google Apps Scriptを新規作成する。
- 名前を nonClassTest に変更し,コード.gsに書かれている4行は全削除。
- 次のプログラムをコピーペーストする。**********の部分は,logのファイルIDに置き換える。
実行ログを残すプログラム,クラスを使ってない
var logID = "**********";//事前にドキュメントを作りIDをしらべておく
function main(){
var log = DocumentApp.openById(logID);
//clearDoc(log);//logをクリアしたい場合に有効にする,延々とlogを残す場合は不要
printDoc(log,"ログ記録を開始\n");
printDoc(log,"今日の天気は<Weather>です。\n");
replaceDoc(log, "<Weather>","晴れ");
var str = "Good Morning";
words = str.split(' ');
printDoc(log, words[1]+'\n');
printDoc(log,"ログ記録を終了\n");
}
//-----------------------デバッグ用の関数群
function clearDoc(doc){//docの中身を全部削除
var body = doc.getBody();
body.clear();
}
function printDoc(doc,str){//docにstrを書き込む
var body = doc.getBody();
var docText = body.editAsText();
docText.appendText(str);
}
function replaceDoc(doc,src,dst){//srcをdstにリプレイス
var body = doc.getBody();
body.replaceText(src,dst);
}
function saveAndCloseDoc(doc){//使う必要はほぼない
doc.saveAndClose();
}
- 次のようにして,main関数を実行する。自動保存されないようなので,実行前にフロッピーディスクマークをクリックして保存する癖を付けると良い。(保存し忘れても次回起動時に変更を有効にするかどうか聞かれる)
- 許可・承認を求められた場合は,内容を確認して許可する。
- 実行結果は次の通り。
- 関数replaceDocについては,logを残すだけなら使うことはない。こういうこともできるよ,というサンプル。
<課題1>
__ logに,各自の出席番号と氏名を記録するプログラムを追加せよ。logに表示されていれば良い。作成したlogはワードファイル(log.docx)としてダウンロードし,指定のフォルダにアップロードせよ。__
##2.1 クラスを使った例
- Google Apps Scriptを新規作成する。
- 名前を classTest に変更し,コード.gsに書かれている4行は全削除。
- 下をコピーペースとして同様に実行すると,同じ実行結果となる。
- **********の部分は,logのファイルIDに置き換える。
実行ログを残すプログラム,クラスを使っている
var logID = "**********";//事前にドキュメントを作りIDをしらべておく
function main(){
var log = new Doc(logID);
//log.clear();//logをクリアしたい場合に有効にする,延々とlogを残す場合は不要
log.print("ログ記録を開始\n");
log.print("今日の天気は<Weather>です。\n");
log.replace("<Weather>","晴れ");
var str = "Good Morning";
words = str.split(' ');
log.print(words[1]+'\n');
log.print("ログ記録を終了\n");
}
//newでオブジェクト生成時に実行されるコンストラクタ(初期化する関数)
//クラス名は最初の文字を大文字にするのが一般的なルール
Doc = function(id){
this.ID = id;
this.doc = DocumentApp.openById(this.ID);
this.body = this.doc.getBody();
this.docText = this.body.editAsText();
}
//メンバ関数(メソッド)は,prototypeでつなげて定義する
//テキストの追加,単にログを追加するなど
Doc.prototype.print = function(str){
this.docText.appendText(str);
}
//キーワード等をリプレイス
Doc.prototype.replace = function(src,dst){
this.body.replaceText(src,dst);
}
//中身をクリア
Doc.prototype.clear = function(){
this.body.clear();
}
//IDを返すメソッド
Doc.prototype.getID = function(){
return this.ID;
}
//ここから下は参考,作ったけどあまり使いそうもない
/*
Doc.prototype.close = function(){
this.doc.saveAndClose();
}
Doc.prototype.getDoc = function(){
return this.doc;
}
Doc.prototype.getBody = function(){
return this.body;
}
Doc.prototype.getText = function(){
return this.docText;
}
*/
/*
//参考文献
http://www.yunabe.jp/docs/javascript_class_in_google.html
*/
- クラスは,コンストラクタと関数定義でできている。
-
var log = new Doc(logID);
によりDocクラスのインスタンス(オブジェクト)が生成される。 - 生成されると,とりあえずコンストラクタが実行される。
コンストラクタ
Doc = function(id){
this.ID = id;
this.doc = DocumentApp.openById(this.ID);
this.body = this.doc.getBody();
this.docText = this.body.editAsText();
}
- コンストラクタは,次の形で定義する。引数はあってもなくてもよい。
コンストラクタ定義の形
クラス名 = function(引数){
・・・
}
- thisはnewで生成された,インスタンスを指す。つまりいつか作られるであろうインスタンスに対して,そのインスタンスが使用する変数を用意するのに,
this.ID
などとしている。thisがあれば,varは不要。 - メソッド(メンバ関数)は,次のように定義する。引数はあってもなくても良い。
メソッド定義の形
クラス名.prototype.関数名 = function(引数){
・・・
}
- これで,Google Apps Scriptの実行ログが残せるようになった!!
- ちなみに,ドキュメントを二つ作っておいて,次のようにmainの中で簡単に二種類のログを残すこともできる。これでthisの意味が理解しやすい。
- **********,++++++++++は,あらかじめ手動でドキュメントを作成しておき,そのIDに置き換える。
- クラスを利用することで面倒な手続が,カプセル化(ブラックボックス化)されて,mainの中がすっきりする。
- 関数でちまちま書いても良いが,クラスで丁寧に記述するとプログラムの見通しが良くなるし,楽になる。
メソッド定義の形
var log1ID = "**********";
var log2ID = "++++++++++";
function main(){
var log1 = new Doc(log1ID);
var log2 = new Doc(log2ID);
log1.print("log1に書き出し\n");
log2.print("log2に書き出し\n");
}
//////////Docクラスの定義開始(コンストラクタとメンバ関数で構成)
//Docクラスのコンストラクタの記述
Doc = function(id){
this.ID = id;
this.doc = DocumentApp.openById(this.ID);
this.body = this.doc.getBody();
this.docText = this.body.editAsText();
}
//Docクラスのメンバ関数の定義開始
//メソッドprintの定義,テキスト追加
Doc.prototype.print = function(str){
this.docText.appendText(str);
}
//メソッドreplaceの定義,文字列置き換え
Doc.prototype.replace = function(src,dst){
this.body.replaceText(src,dst);
}
//メソッドclearの定義,全消去
Doc.prototype.clear = function(){
this.body.clear();
}
//メソッドgetIDの定義,ファイルIDを返す
Doc.prototype.getID = function(){
return this.ID;
}
//////////Docクラスの定義終了
- 実行時やデバック時のログを残すのに便利なDocクラスとしてまとめておく。
- 時間を遅らせて表示したい場合があるので,ウェイト関数を追加。
デバッグや実行時のログを残すためのクラスDoc
//////////Docクラスの定義開始(コンストラクタとメンバ関数で構成)
//Docクラスのコンストラクタの記述
Doc = function(id){
this.ID = id;
this.doc = DocumentApp.openById(this.ID);
this.body = this.doc.getBody();
this.docText = this.body.editAsText();
}
//Docクラスのメンバ関数の定義開始
//メソッドprintの定義,テキスト追加
Doc.prototype.print = function(str){
this.docText.appendText(str);
}
//メソッドreplaceの定義,文字列置き換え
Doc.prototype.replace = function(src,dst){
this.body.replaceText(src,dst);
}
//メソッドclearの定義,全消去
Doc.prototype.clear = function(){
this.body.clear();
}
//メソッドgetIDの定義,ファイルIDを返す
Doc.prototype.getID = function(){
return this.ID;
}
//指定秒数のウェイト,表示動作を遅らせたい時などに使用
Doc.prototype.waitSec = function(sec){
var start = new Date().getSeconds();
while((new Date().getSeconds()-start) < sec);
}
//指定ミリ秒のウェイト,表示動作を遅らせたい時などに使用
Doc.prototype.waitMiliSec = function(msec){
var start = new Date(); //new Date()は,「1970年1月1日午前0時」からの通算ミリ秒を返す
while((new Date()-start) < msec);
}
//今現在の日時を表示
Doc.prototype.printTodayNow = function(){
var now = new Date();
var year = now.getYear();
var month = now.getMonth() + 1;
var day = now.getDate();
var hour = now.getHours();
var min = now.getMinutes();
var sec = now.getSeconds();
this.docText.appendText(year +'_'+ ("0"+month).slice(-2) +'_'+ ("0"+day).slice(-2) +' '+
("0"+hour).slice(-2) +'-'+ ("0"+min).slice(-2) +'-'+ ("0"+sec).slice(-2));
}
//////////Docクラスの定義終了
<課題2>
__ GoogleDrive上に,今日の日付のフォルダを作成し,その中でプログラムを作成する。ここを参考に,(1)文字をボールドに設定,解除するメソッド,(2)文字のフォントサイズを変更するメソッド,(3)アンダーラインを設定・解除するメソッド,(4)フォントカラーを設定するメソッド,を追加し,動作確認せよ。動作確認した結果をワードファイルとしてダウンロードし,指定されるフォルダにアップロードせよ。なお文章中に必ず,出席番号と氏名を書いておくこと。(ヒント:ボールドの例を下に示す)__
クラスDocのsetBoldメソッドの例
//メソッドsetBoldの定義
Doc.prototype.setBold = function(startOffset, endOffsetInclusive, bold){
this.docText.setBold(startOffset, endOffsetInclusive, bold);
}