ExpressionScript - 呈現
From LimeSurvey Manual
簡介
LimeSurvey 使用運算式腳本(Expression Script),使 LimeSurvey 能夠支援更複雜的分支、評估、驗證和客製化。它取代了 LimeSurvey 在後端管理取代欄位、條件和評估的方式。由於省去了大部分執行時的資料庫讀取,它也大幅加快了處理速度。運算式腳本由 Thomas White 博士(TMSWhite)開發。
關鍵定義
- 運算式:以大括號括起來的任何內容:
- 前提是左大括號之後或右大括號之前沒有緊接空白字元。
- 運算式的內容由 EM 進行運算,因此可以包含數學公式、函數,以及複雜的字串和日期處理。
- 客製化:有時稱為「管線化(piping)」。這是有條件地修改文字的過程:
- 您可以存取所有「取代欄位」、參與者資料和作答資料。
- 您也可以更輕鬆地存取問題、答案及其屬性。
- 相關性等式:一個控制問題可見性的新問題屬性:
- 如果設定了相關性等式,則只有在相關性運算結果為 true 時才會顯示該問題。
- 在內部,所有 array_filter 和 array_filter_exclude 指令都會變成子問題層級的相關性。
- SGQA(過去變數的命名方式):
- 代表 Survey-Group-Question-Answer(問卷-題組-問題-答案)
- SGQA 變數名稱看起來像 123X5X382X971,並且可能帶有子問題後綴。
- 這些變數名稱與底層的 S/Q/G/A 資料庫代碼相關,因此經常需要修改
- 公式題型:一種新的題型,可將計算結果或報告儲存到資料庫:
- 它類似於樣板文字問題,但即使您設定了「始終隱藏本題」,其內容也會儲存到資料庫。
- 問題編號:這是 EM 偏好使用的變數名稱:
- 它可以是說明問題用途的描述性名稱,使複雜的邏輯更易於閱讀。
- 有效的問題編號不應以數字開頭,因此使用問題編號為問題編號時,只需使用「q1」、「q1a」或「g1q2」。
- 如果您將資料匯出到 SPSS 或 R,這就會成為變數名稱。因此,如果您要進行統計分析,只需建立唯一的問題編號即可。
我必須使用運算式腳本嗎?
簡短的回答是「不必」。不過,這在很大程度上取決於您要建立的問卷的複雜程度。
例如,條件編輯器涵蓋了一些可套用於問卷問題的基本運算式。然而,條件編輯器的功能有限。這就是使用運算式腳本的原因——它擴大了自訂的可能性。
我可以混合使用條件和相關性等式嗎?
可以。您可以對某些問題使用條件編輯器,對其他問題使用相關性等式。
您無法在同一個問題中同時設定條件和運算式!一旦設定了條件,它就會取代相關性等式欄位中所寫的任何運算式。此外,相關性等式欄位也無法再手動編輯。
不過,仍有辦法在一個問題中同時使用運算式和條件。如上所述,條件會取代相關性等式欄位。完成後,請查看新建立的等式,並將其複製到文字編輯器中。從條件編輯器中刪除新建立的條件,然後編輯該問題,將文字編輯器檔案中基於條件的運算式與您想使用的其他運算式一併加入。
我應該如何在條件和相關性之間做選擇?
以下列出兩種方式的優缺點:
| 方式 | 優點 | 缺點 |
|---|---|---|
| 條件 | 1. 具有方便的圖形介面,可用來建立簡單的條件。 2. 圖形介面文件齊全,支援團隊也很了解 |
1. 只支援簡單的比較,且不擅長處理「AND」和「OR」條件。 2. 串聯條件的運作不穩定 3. 速度慢——大量使用資料庫——可能會拖慢較長的問卷。 4. 有些重新載入條件的問題被回報。 5. 當有數十、數百或數千個問題時,圖形介面的擴展性不佳。 6. 由於必須使用 SGQA 名稱,轉換紙本問卷可能會很慢。 7. 複雜的分支邏輯通常需要程式設計師撰寫客製程式碼。 |
| 相關性 | 1. 支援非常複雜的邏輯,包括 80 多個函數和數學/字串運算子。 2. 完美支援串聯邏輯。 3. 速度快——無需額外的資料庫呼叫,可支援 1000 個以上問題的問卷。 4. 重新載入邏輯不會有問題,因為它不需要 SGQA 代碼。 5. 語法醒目提示可擴展到 1000 個以上問題的問卷。 6. 對於想要將現有紙本問卷電腦化的團體來說,使用簡單又快速。 7. 無需程式設計師即可輕鬆支援半結構化訪談和流行病學調查。 |
1. 沒有用於簡單條件的圖形介面——而是使用語法醒目提示。 |
- 我們建議您使用較符合您需求的方式。
- 如需 EM 功能的更詳細說明,請按一下以下連結。
開始使用
開始使用 EM 的最佳方式是:
- 從 https://www.limesurvey.org/en/download 安裝最新的穩定版本。
- 匯入並探索一些範例問卷。
- 探索使用案例和操作指南,以及逐步範例。
- 探索 EM 文件(本頁)
- 獨立運算式的單元測試(進階)
- 顯示使用所有 EM 函數和運算子的範例,以及 PHP 和 JavaScript 的結果;
- 請注意,有少數函數在 PHP 和 JavaScript 版本中會產生不同的結果,因此本頁可讓您據此規劃 EM 邏輯。
- 獨立運算式的單元測試(進階)
術語
以下詞彙常用來描述 EM 的功能:
- 基於相關性的分支 - 如果某個問題具有相關性,則提出該問題,否則不提出(例如,將其隱藏並在資料庫中標記為 NULL)。您可以在問題編輯面板以及題組編輯面板中找到相關性欄位。後者用於將一組條件套用到整個題組,而無需將相同的條件複製到每個問題,以及/或結合題組層級和問題層級的條件邏輯。
- 客製化 - 一旦知道應該提出哪些問題,客製化(有時稱為管線化)就會指定如何提出問題。這不僅支援簡單的替換(如 {TOKEN:FIRSTNAME}),也能根據對象的性別或數量進行動詞變位和名詞變格。它還能讓您根據問卷受訪者是否回答了其他問題(或如何回答)來更改傳遞給他們的訊息。
- 等式 - EM 新增了一種稱為公式的新題型,用於儲存運算式的結果。即使您在頁面上隱藏了公式,其結果仍會被計算並寫入資料庫。因此,它們可用於隱藏的計分計算、基於複雜等式的導覽、評估,以及將在資料庫中產生並儲存的報告。
相關性與串聯相關性
現在每種題型都有一個相關性選項,用來控制是否顯示該問題。EM 會依照相關性等式在問卷中出現的順序逐一處理。如果運算式為 true(或缺少——以支援舊版問卷),則會顯示該問題。如果不具相關性,則該問題將被隱藏,且其值在資料庫中會被設為 NULL。如果某個題組中沒有任何相關的問題,則整個題組將被略過。
此外,如果運算式中的任何變數不具相關性,則該運算式的運算結果一律為 false。這啟用了串聯相關性,讓您不必為每個問題撰寫非常冗長的相關性等式。
假設您有五個問題 Q1-Q5,您只想在 Q1 已回答時顯示 Q2,在 Q2 已回答時顯示 Q3,依此類推。相關性等式可能如下:
| 問題編號 | 相關性 | 問題 |
|---|---|---|
| Q1 | 1 | 您的姓名是? |
| Q2 | Q1 | {Q1},您幾歲? |
| Q3 | Q2 | 所以,您今年 {Q2} 歲。 您已婚嗎? |
| Q4 | Q3 == "Y" | {Q1},您結婚多久了? |
| Q5 | Q4 | {Q1},您有幾個孩子? |
題組層級的相關性
ExpressionScript 也支援題組層級的相關性。這使得實作迴圈更加容易。假設您想從最多 10 個實體(例如產品或家庭成員)收集資訊,您會先確定有多少實體需要後續追問(例如詢問家中有多少人居住,或讓人們從一長串清單中勾選他們喜歡的產品)。在得知有多少實體需要後續追問之後,您可以對 10 個後續追問題組中的每一組使用題組層級的相關性,如 {count >= 1}、{count >=2}、... {count >= 10}。在每個題組內,您可以設定問題層級的條件邏輯(例如,針對每位對象的性別或年齡所設計的後續問題)。問題層級與題組層級的相關性等式會進行 AND 運算,以決定應顯示哪些內容。
若要查看此範例,請匯入以下問卷:人口普查問卷範例。
從下方的螢幕截圖可以看到,如果受訪者至少與另一位同住者同住,則會顯示(即具有相關性)Person 1 題組:

量身訂做/管道傳遞
大括號內的任何內容現在都會被視為運算式(下文所述的一種例外情況除外)。運算式可以存取所有 LimeReplacementFields 和變數(透過多個別名)、所有典型的運算子(數學、邏輯和比較運算子),以及數十個函式(這些函式甚至可在用戶端動態運作)。
使用這些公式,您可以執行下列操作:
- 根據先前的作答結果,有條件地向受訪者顯示量身訂做的訊息;
- 建立評估並顯示評估結果(或根據這些結果進行條件分支或顯示訊息),而完全不需使用評估模組本身;
- 在問題、答案和報告中進行動詞變化與名詞變格;
- 在問卷結尾的「顯示您的答案」頁面之前顯示作答結果摘要。
公式
新增了一種名為公式的問題類型。您可以將它視為文字顯示問題類型,不同之處在於它會將所顯示內容的值儲存在資料庫中。因此,如果公式問題的問題文字包含評估計算,該值就會儲存在資料庫的變數中,並可顯示於公開或私人統計中。
語法
大括號內的任何內容現在都會被視為運算式(有一個例外:前後不得有空白字元,這是為了確保 ExpressionScript 不會嘗試處理內嵌的 JavaScript)。
請注意,運算式可以跨越多行,只要左大括號之後或右大括號之前沒有空白字元即可。這對於像下面這樣的巢狀 "if()" 陳述式特別有幫助:
{if(is_empty(PFTotals),
'',
if(PFTotals >= -5 && PFTotals <= -4,
'Very Soft',
if(PFTotals >= -3 && PFTotals <= -2,
'Soft',
if(PFTotals == -1,
'Somewhat Soft',
if(PFTotals == 0,
'Moderate',
if(PFTotals == 1,
'Somewhat Hard',
if(PFTotals >= 2 && PFTotals <= 3,
'Hard',
if(PFTotals >= 4 && PFTotals <= 5,
'Very Hard',
''
)
)
)
)
)
)
)
)}
ExpressionScript 支援下列語法:
- 所有標準數學運算子(例如 +,-,*,/,!);
- 所有標準比較運算子(例如 <,<=,==,!=,>,>=,以及其對應的等效運算子:lt, le, eq, ne, gt, ge);
- 括號(可用來將子運算式分組);
- 條件運算子(例如 &&,| | 以及其對應的等效運算子:and, or);
- 單引號與雙引號字串(兩者可互相嵌入另一種引號的字串);
- 逗號運算子(可使用運算式清單,並只傳回最後的結果);
- 指派運算子(=);
- 預先定義的變數(用於參照問題、問題屬性和作答結果),例如 SGQA 代碼;
- 預先定義的函式(目前已有 80 多個,而且很容易新增更多)。
運算子
EM 語法遵循一般的運算子優先順序:
| 層級 | 運算子 | 說明 |
|---|---|---|
| 1 | () | 用於分組或呼叫函式的括號 |
| 2 | ! - + | 一元運算子:非、負號、正號 |
| 3 | * / | 乘、除 |
| 4 | + - | 加、減 |
| 5 | < <= > >= lt le gt ge | 大小比較 |
| 6 | == != eq ne | 相等比較 |
| 7 | and | 邏輯 AND |
| 8 | or | 邏輯 OR |
| 9 | = | 指派運算子 |
| 10 | , | 逗號運算子 |
數字與字串不符以及字母/數值比較的警告
使用大小比較或相等比較來比較值時,請留意型別不符的情況。使用者輸入的值(或透過答案代碼提供的值)如果明確為數字,就可以當作數字使用。
如果您以 " 括住其中一個值,就會進行文字/字串比較。若要以數字進行比較,請勿以 " 括住數字。
例如,如果 Q0.NAOK 是數值問題且其值為 9,則 Q0.NAOK > "50" 的結果為 true。這是因為運算子 > 會將其視為字母順序比較,而非數值比較。
為確保比較的是整數值,您可以使用 intval(Q0.NAOK) > 50。請記住,如果 Q0.NAOK 不是數字(空白或字串),則 intval(Q0.NAOK) === 0。若要比較字串值("A" < "B"),請直接使用 strcmp:strcmp(Q0.NAOK,"B") 或 strcmp(Q0.NAOK,"A5")。
使用指派運算子 (=) 的注意事項
除非絕對必要,否則您應避免使用指派運算子,因為它們可能會造成非預期的副作用。例如,如果您變更先前作答結果的值,該問題與目前問題之間的串聯相關性及驗證邏輯並不會重新計算,因此最後可能會得到內部不一致的資料(例如,應該被清為 NULL 卻仍保持已作答的問題,或是被略過但本應作答的問題)。一般而言,如果您想為變數指派值,應建立公式問題類型,並使用運算式來設定其值。不過,在少數情況下確實需要此運算子,因此我們仍提供了它。
為了提醒您留意此運算子,它在語法公式中會以紅色字體顯示(以免與 "==" 混淆)。
使用指派運算子
您可能需要使用指派運算子的主要原因如下:
- 您需要透過公式為不接受預設值的問題設定預設值(例如清單單選,其使用者介面允許您選擇其中一個答案選項,但不允許輸入公式)。不過請務必小心,因為 LimeSurvey 無法驗證您的公式所產生的結果是否為該問題的允許答案之一;
- 您需要根據後面的作答結果,強制變更先前問題的作答結果;
- 等等……
您可以使用整個運算式管理員系統來達成此目的。建議為此目的使用公式。
一些範例:
- 將簡短文本問題的答案設為小寫:
{QCODE=strtolower(QCODE.NAOK)}; - 在問卷開始時為陣列問題類型設定預設答案:
{Q1_SQ1=(is_empty(Q1_SQ1.NAOK),"A99",Q1_SQ1.NAOK)}; - 在問卷開始時為陣列文字問題類型設定預設答案:
{Q1_SQY1_SQX1 = (is_empty(Q1_SQY1_SQX1.NAOK),"Inserted answer", Q1_SQY1_SQX1.NAOK)}; - 依條件設定答案:
{QCODE=if(YesNo="Y","A1","")}。
XSS 安全性
啟用 XSS 後,運算式管理員系統的某些部分將無法使用:
- 在一個運算式中開始 HTML 標籤,卻在另一個運算式中結束;
- 在 URL 中使用複雜的運算式。
範例與替代做法:
{if( 1 ,"<strong>","")}information{if( 1 ,"</strong>","")}在 XSS 安全性下無法運作,此時您可以改用{if(1,"<strong>information</strong>","information")};<a href="/script.php?value={if(QCODE == "Y","yes","no")}">next</a>,此時您可以使用等式問題,因為使用完整的問題編號是允許的:<a href="/script.php?value={EQUATION.NAOK}">next</a>。
存取變數
ExpressionScript 提供對您可能需要的任何變數的唯讀存取。為了向下相容,它提供對下列項目的存取:
- TOKEN:xxx - TOKEN 的值(例如 TOKEN:FIRSTNAME、TOKEN:ATTRIBUTE_5)(僅適用於非匿名問卷)。
- 所有範本使用的 {XXX} 值。
- 在問題文字中,您可以使用 {QID} 代表問題 ID,以及使用 {SGQ} 代表該問題的 SGQA。
- {SAVEDID} 代表目前的回覆 ID
此外,ExpressionScript 讓您可以透過問題編號(資料庫中問題資料表的「title」欄)來參照變數。這也是您將資料匯出至 SPSS、R 或 SAS 時所使用的變數標籤。例如,如果您有關於姓名、年齡和性別的問題,您可以將這些變數稱為 name、age 和 gender,而不是 12345X13X22、12345X13X23 和 12345X13X24。這使得每個人都更容易閱讀等式並驗證邏輯,同時也能在不必追蹤群組或問題編號的情況下調整問題順序。
重要: 參照出現在前面頁面或問題中的變數會更安全。
此外,ExpressionScript 讓您可以存取問題的許多屬性:
| 語法 | 意義 | 範例 | 範例結果 |
|---|---|---|---|
| Qcode | Qcode.code 的別名 | {implode(',',name,gender)} | 'Tom','M' |
| Qcode.code | 若問題具相關性,則為該問題所選的作答代碼(否則為空白);若非編碼問題,則為文字值 | {implode(',',name.code,gender.code)} | 'Tom','M' |
| Qcode.NAOK | 與 Qcode 相同 - 請參閱 NAOK 的說明 | {gender.NAOK} | 'M' |
| Qcode.value | 若問題具相關性,則為該問題的評估值(否則為空白);若非編碼問題,則為文字值 | {gender.value} | '1' |
| Qcode.valueNAOK | 與 Qcode.value 相同 - 請參閱 NAOK 的說明 | {gender.valueNAOK} | '1' |
| Qcode.shown | 問題的顯示值 | {implode(',',name.shown,gender.shown)} | 'Tom','Male' |
| Qcode.question | 問題的文字 | {gender.question} | 'What is your gender?' |
| Qcode.mandatory | 問題是否為必填(Y/N) | {gender.mandatory} | 'N' |
| Qcode.qid | 內部問題編號(非循序編號) | {gender.qid} | 337 |
| Qcode.type | 問題類型 | {gender.type} | 'G' |
| Qcode.jsName | 問題正確的 JavaScript 名稱,無論是否在此頁面上宣告 | {gender.jsName} | 'java1827X3X337' |
| Qcode.gid | 內部群組編號(非循序編號) | {gender.gid} | 3 |
| Qcode.qseq | 問題的循序編號,從 0 開始 | {gender.qseq} | 5 |
| Qcode.gseq | 群組的循序編號,從 0 開始 | {gender.gseq} | 1 |
| Qcode.relevanceStatus | 問題目前是否具相關性(0 或 1) | {gender.relevanceStatus} | 1 |
| Qcode.relevance | 問題層級的相關性等式 | {gender.relevance} | '!is_empty(name)' |
| Qcode.grelevance | 群組層級的相關性等式 | {gender.grelevance} | 'num_children >= 5' |
| Qcode.sgqa | 此問題的 SGQA 值 | {gender.sgqa} | '1827X3X337' |
HTML 編輯器問題
如果您使用 HTML 編輯器,部分字元會被替換為 HTML 實體:
- & 會變成 &
- < 會變成 <
- > 會變成 >
如果您使用 HTML 編輯器,則需要使用:
- and 代表 &
- lt 代表 <
- le 代表 <=
- gt 代表 >
- ge 代表 >=
建議您清除運算式中出現的 HTML。如果您使用 LimeSurvey 的 HTML 編輯器,請按一下「Source」按鈕(位於編輯器左上方),並刪除所有與您的運算式無關的字元(例如
、
等)。
Qcode 變數命名
以下是依問題類型建構 Qcode(以及存取部分屬性)的詳細說明。一般而言,Qcode 的建構方式如下:
QuestionCode . '_' . SubQuestionID . '_' . ScaleId
對於評論和其它,對應的問題編號分別為 QuestionCode_comment 和 QuestionCode_other。
| 類型 | 說明 | 代碼 | 子問題 | 答案選項 | 量表 | 答案代碼 | 顯示的答案 | 相關性 |
|---|---|---|---|---|---|---|---|---|
| 5 | 5 點選擇單選按鈕 | Q1 | 1-5 | {Q1} | {Q1.shown} | {Q1==3} | ||
| B | 陣列(10 點選擇)單選按鈕 | Q2 | L1-L6 | 1-10 | {Q2_L2} | {Q2_L2.shown} | {Q2_L2==7} | |
| A | 陣列(5 點選擇)單選按鈕 | Q3 | 1-5 | 1-5 | {Q3_1} | {Q3_1.shown} | {Q3_1>=3} | |
| 1 | 陣列(彈性標籤)雙量表 | Q4 | sq1-sq5 | 0:a1-a3 | 1:b1-b3 | {Q4_sq1_0} | {Q4_sq1_1.shown} | {Q4_sq1_1=='b2'} |
| H | 陣列(彈性)- 欄格式 | Q5 | 1-5 | s,m,t | {Q5_1} | {Q5_1.shown} | {Q5_1=='s'} | |
| F | 陣列(彈性)- 列格式 | Q6 | F1-F5 | 1-5 | {Q6_F3} | {Q6_F3.shown} | {Q6_F3==4} | |
| E | 陣列(增加/不變/減少)單選按鈕 | Q7 | 1-7 | I,S,D | {Q7_4} | {Q7_4.shown} | {Q7_4=='D'} | |
| : | 陣列(多重彈性)1 至 10 | Q8 | ls1,todo,ls2 | min,max,avg | {Q8_ls1_max} | {Q8_ls2_avg.shown} | {Q8_ls2_min==7} | |
| ; | 陣列(多重彈性)文字 | Q9 | hp,st,sw | 1st,2nd,3rd | {Q9_hp_3rd} | {Q9_hp_3rd.shown} | {Q9_hp_3rd=='Peter'} | |
| C | 陣列(是/不確定/否)單選按鈕 | Q10 | 1-5 | Y,N,U | {Q10_1} | {Q10_1.shown} | {Q10_3=='Y'} | |
| X | 樣板文字問題 | Q11 | {Q11.shown} | |||||
| D | 日期 | Q12 | {Q12} | {Q12.shown} | ||||
| * | 等式 | Q13 | {Q13} | {Q13.shown} | {Q13>5} | |||
| ~124~ | 檔案上傳(記錄已上傳的檔案數量) | Q14 | {Q14} | {Q14>0} | ||||
| G | 性別下拉式選單 | Q15 | M,F | {Q15} | {Q15.shown} | {Q15=='M'} | ||
| U | 大量的自由文本 | Q16 | {Q16} | {Q16.shown} | {strlen(Q16)>100} | |||
| I | 語言問題 | Q17 | {Q17} | {Q17.shown} | {Q17=='en'} | |||
| ! | 列表 - 下拉式選單 | Q18 | 1-5 | {Q18} | {Q18.shown} | {Q18==3} | ||
| L | 列表 下拉式選單/單選按鈕列表 | Q19 | A-Z | {Q19} | {Q19.shown} | {Q19=='X'} | ||
| O | 帶評論的列表 下拉式選單/單選按鈕列表 + 文字區域 | Q20 | A-F | {Q20},{Q20comment} | {Q20.shown} | {Q20=='B'} | ||
| T | 長篇的自由文本 | Q21 | {Q21} | {Q21.shown} | {strstr(Q21,'hello')>0} | |||
| M | 多項選擇 核取方塊 | Q22 | A-F, other | {Q22_E}, {Q22_other} | {Q22_E.shown}, {Q22_other.shown} | {Q22_E=='Y'} | ||
| P | 帶評論的複選題 核取方塊 + 文字 | Q23 | A-F | {Q23_D}, {Q23_Dcomment} | {Q23_D.shown} | {!is_empty(Q23)} | ||
| K | 多重數值問題 | Q24 | self,mom,dad | {Q24_self} | {Q24_self.shown} | {Q24_self>30} | ||
| Q | 多重簡短文本 | Q25 | A-F | {Q25_B} | {Q25_B.shown} | {substr(Q25_B,1,1)=='Q'} | ||
| N | 數值問題類型 | Q26 | {Q26} | {Q26.shown} | {Q26 > 30} | |||
| R | 排序樣式 | Q27 | 1-4 | {Q27_1} | {Q27_1.shown} | {Q27_1==3} | ||
| S | 簡短的自由文本 | Q28 | {Q28} | {Q28.shown} | {Q28=='mine'} | |||
| Y | 是/否單選按鈕 | Q29 | {Q29} | {Q29.shown} | {Q29=='Y'} |
NAOK 的用法
NAOK --> 「不適用」(Not Applicable,NA)沒問題(OK)
使用 NAOK 表示全部或部分變數不具相關性(例如「不適用」(NA)沒問題(OK))。
例如:如果 Q1 的其中一個子問題被篩選掉,count(Q1_SQ1,Q1_SQ2,Q1_SQ3,Q1_SQ4) 一律會傳回空字串。 若要在這類問題中計算已勾選的子問題數量,可以使用 count(Q1_SQ1.NAOK,Q1_SQ2.NAOK,Q1_SQ3.NAOK,Q1_SQ4.NAOK)。如果子問題被隱藏,EM 會傳回空字串。
若未使用 NAOK,只要有一個問題或一個子問題被隱藏,EM 一律會傳回空字串(等同於傳回 false)。
.shown 一律使用 NAOK 機制(隱藏時傳回空字串),但如果您需要答案的代碼,最好在問題編號後加上 .NAOK(除非您需要不加,且清楚自己在做什麼)。
更多資訊請參閱覆寫串聯條件小節。
保留變數「this」、「self」和「that」
您經常需要評估問題的所有部分,例如計算已回答的子問題數量或加總分數。有時您只想處理問題的特定列或欄(例如取得列或欄的總和並儲存到資料庫中)。這些保留變數可讓此過程變得相對輕鬆。
「This」變數
「this」變數僅用於「整個問題驗證等式」和「子問題驗證方程式」選項(後者無法從圖形介面設定)。它會展開為這些問題中每個儲存格的變數名稱。因此,如果您想確保每個項目都大於三,可將「子問題驗證方程式」設定為 (this > 3)。
「Self」變數
「self」和「that」變數功能更強大,可作為在處理等式之前展開的巨集。「self」變數的語法選項為:
- self
- self.suffix
- self.sub-selector
- self.sub-selector.suffix
- suffix(後綴)是任何一般的 qcode 後綴(例如:NAOK、value、shown)
- sub-selector(子選擇器)可以是下列其中之一:
- comments - 僅限屬於評論的子問題(例如:帶評論的複選題和帶評論的列表);
- nocomments - 僅限不屬於評論的子問題;
- sq_X - 其中 X 為列或欄的識別碼。僅會選取符合 X 模式的子問題。請注意,搜尋是針對完整的代碼識別碼進行,sq_X 會比對並包含子問題 nX、X、Xn(例如:若您使用 sq_1,則會包含子問題 a1、1a、1、11 或 001)。請特別注意雙量表問題類型,其子問題代碼為 QCODE_SQCODE_1 和 QCODE_SQCODE_1,以及排序問題類型,其子問題代碼為 QCODE_1、QCODE_2 ....
範例:
- 問題的任何部分是否已作答? -> {count(self.NAOK)>0}
- 此問題的評估分數為何? -> {sum(self.value)}
您也可以使用這些變數來取得列與欄的合計。假設您有一個數字陣列,列為 A-E,欄為 1-5。
- 總計為何? -> {sum(self.NAOK)}
- B 列的合計為何? -> {sum(self.sq_B.NAOK)}
- 第 3 欄的合計為何? -> {sum(self.sq_3.NAOK)}
「That」變數
"that" 變數與 "self" 變數相似,但可讓您參照其他問題。其語法如下:
- that.qname
- that.qname.suffix
- that.qname.sub-selector
- that.qname.sub-selector.suffix
qname 是不含任何子問題延伸的問題名稱。例如,我們建立一個問題 'q1',其中 'q' 同時也代表其 qname。
範例:
- 問題 q1 的任何部分是否已作答? -> {count(that.q1.NAOK)>0}
- q2 的評估分數為何? -> {sum(that.q2.NAOK)}
- q3 的總計為何? -> {sum(that.q3.NAOK)}
- q4 中 C 列的合計為何? -> {sum(that.q4.sq_C.NAOK)}
- q4 中第 2 欄的合計為何? -> {sum(that.q4.sq_2.NAOK)}
"self" 和 "that" 變數可用於任何相關性、驗證或客製化內容中。
唯一需要注意的是,當您使用顯示邏輯檔案功能時,系統會顯示 "self" 和 "that" 展開後的值。這可讓您看到實際產生的等式,以便您(以及 EM)驗證變數是否存在。由於您可能會看到相當冗長的等式,這看起來也許令人困惑。不過,若您編輯該問題,將會看到使用 "self" 和/或 "that" 的原始等式。
- 您想要明確指定等式中使用的每個變數名稱,或
- 使用不含子問題的變數(例如:單一回答的問題)。在這些情況下,為變數加上 "that" 前綴是多此一舉,而且可能會得到非預期的結果。
存取函數
ExpressionScript 提供數學、字串及使用者自訂函數的存取功能,如下所示。這些函數皆有對應的 PHP 與 JavaScript 版本,因此在伺服器端(PHP)與用戶端(JavaScript)的運作方式完全相同。新增函數也很容易。
已實作的函數
目前可使用下列函數:
| 函式 | 意義 | 語法 |
|---|---|---|
| abs | 絕對值 | number abs(number) |
| acos | 反餘弦 | number acos(number) |
| addslashes | 以斜線引用字串 | string addslashes(string) |
| asin | 反正弦 | number asin(number) |
| atan | 反正切 | number atan(number) |
| atan2 | 兩變數正切函數 | number atan2(number, number) |
| ceil | 取稍大的整數 | number ceil(number) |
| checkdate | 若為有效的格里曆日期,則傳回 true(1) | bool checkdate(month,day,year) |
| convert_value | 使用數值的 inputTable 和 outputTable 轉換數值 | number convert_value(fValue, iStrict, sTranslateFromList, sTranslateToList) |
| cos | 餘弦 | number cos(number) |
| count | 計算清單中已回答(非空白)問題的數量 | number count(arg1, arg12, ..., argN) |
| countif | 計算清單中與第一個引數相等的已回答問題的數量 | number countif(matches, arg1, arg2, ... argN) |
| countifop | 計算清單中符合條件(arg op value)的已回答問題的數量 | number countifop(op, value, arg1, arg2, ... argN) |
| date | 表示本地日期時間 | string date(format [, timestamp=time()]) |
| exp | 計算以 e 為底的指數 | number exp(number) |
| fixnum | 必要時以逗號作為小數點分隔符號顯示數字 | string fixnum(number) |
| floor | 取稍小的整數 | number floor(number) |
| gmdate | 表示 GMT 日期時間 | string gmdate(format [, timestamp=time()]) |
| html_entity_decode | 將所有 HTML 實體轉換為對應的字元(一律使用 ENT_QUOTES 和 UTF-8) | string html_entity_decode(string) |
| htmlentities | 將所有適用的字元轉換為 HTML 實體(一律使用 ENT_QUOTES 和 UTF-8) | string htmlentities(string) |
| expr_mgr_htmlspecialchars | 將特殊字元轉換為 HTML 實體(一律使用 ENT_QUOTES 和 UTF-8) | string htmlspecialchars(string) |
| expr_mgr_htmlspecialchars_decode | 將特殊 HTML 實體轉換回字元(一律使用 ENT_QUOTES 和 UTF-8) | string htmlspecialchars_decode(string) |
| idate | 以數值傳回當地日期時間 | string idate(string [, timestamp=time()]) |
| if | Excel 風格的 if(test,result_if_true,result_if_false) | if(test,result_if_true,result_if_false) |
| implode | 將字串加入陣列元素 | string implode(glue,arg1,arg2,...,argN) |
| intval | 取得變數數值 | int intval(number [, base=10]) |
| is_empty | 判斷變數是否被視為空白 | bool is_empty(var) |
| is_float | 判斷變數的類型是否為浮點數 | bool is_float(var) |
| is_int | 判斷變數的類型是否為整數 | bool is_int(var) |
| is_nan | 決定變數是否不是數字 | bool is_nan(var) |
| is_null | 決定變數是否為無效 | bool is_null(var) |
| is_numeric | 判斷變數是否為數字或數值字串 | bool is_numeric(var) |
| is_string | 判斷變數的類型是否為字串 | bool is_string(var) |
| join (New in 2.0 build 130129) | 將元素合併為新字串 | join(arg1, arg2, ... argN) |
| list | 傳回以逗號分隔的非空白值清單 | string list(arg1, arg2, ... argN) |
| listifop (New in 3.16.1 ) | 從清單中符合條件(cmpProp op value)的問題中,傳回以「glue」分隔的指定問題屬性(retProp)清單 | string listifop(cmpProp, op, value, retProp, glue, sgqa1, sgqa2, ... sgqaN) |
| ltrim | 去除字串開頭的空白(或其他字元) | string ltrim(string [, charlist]) |
| max | 尋找最大值 | number max(arg1, arg2, ... argN) |
| min | 尋找最小值 | number min(arg1, arg2, ... argN) |
| mktime | 取得日期的 UNIX 時間戳記(6 個引數皆為選用) | number mktime([hour [, minute [, second [, month [, day [, year ]]]]]]) |
| modulo-function | 目前尚不支援取餘數函式。您可以改用 floor() 函式 | floor(x/y)==(x/y) |
| nl2br | 在字串中所有換行符號之前插入 HTML 換行標籤 | string nl2br(string) |
| number_format | 以組分隔字串組格式化一數值 | string number_format(number) |
| pi | pi 值 | number pi() |
| pow | 指數表達式 | number pow(base, exp) |
| quoted_printable_decode | 將 quoted-printable 字串轉換為 8 位元字串 | string quoted_printable_decode(string) |
| quoted_printable_encode | 將 8 位元字串轉換為 quoted-printable 字串 | string quoted_printable_encode(string) |
| quotemeta | 引用元字符 | string quotemeta(string) |
| rand | 隨機產生整數,請參閱此範例 | int rand() OR int rand(min, max) |
| regexMatch | 將字串與正規表示式進行比對 | bool regexMatch(pattern,input) |
| round | 依指定方式進位 | number round(val [, precision]) |
| rtrim | 去除字串結尾的空白(或其他字元) | string rtrim(string [, charlist]) |
| sin | 正弦 | number sin(arg) |
| sprintf | 退回格式化字串 | string sprintf(format, arg1, arg2, ... argN) |
| sqrt | 平方根 | number sqrt(arg) |
| stddev | 計算數字清單的樣本標準差 | number stddev(arg1, arg2, ... argN) |
| str_pad | 以另一個字串將字串填補至指定長度 | string str_pad(input, pad_length [, pad_string]) |
| str_repeat | 重複字串 | string str_repeat(input, multiplier) |
| str_replace | 以取代字串取代所有出現的搜尋字串 | string str_replace(search, replace, subject) |
| strcasecmp | 二進位制、不區分大小寫的的安全字串比較 | int strcasecmp(str1, str2) |
| strcmp | 二進位制的安全字串比較 | int strcmp(str1, str2) |
| strip_tags | 去掉一個字串的 HTML 和 PHP 標籤 | string strip_tags(str, allowable_tags) |
| stripos | 尋找不區分大小寫的 Unicode 字串第一次出現的位置(從 0 開始,找不到時傳回 false) | int stripos(haystack, needle [, offset=0]) |
| stripslashes | 取消括弧中的字串的括弧 | string stripslashes(string) |
| stristr | 不區分大小寫的 strstr | string stristr(haystack, needle [, before_needle=false]) |
| strlen | 取得字串長度 | int strlen(string) |
| strpos | 尋找 Unicode 字串第一次出現的位置(從 0 開始,找不到時傳回 false) | int strpos(haystack, needle [ offset=0]) |
| strrev | 傳回反寫字串 | string strrev(string) |
| strstr | 尋找字串第一次發生的位置 | string strstr(haystack, needle[, before_needle=false]) |
| strtolower | 將字串轉換為小寫 | string strtolower(string) |
| strtotime | 將任何英文日期時間文字描述解析為 Unix 時間戳記 | int strtotime(string) |
| strtoupper | 將字串轉換為大寫 | string strtoupper(string) |
| substr | 傳回 Unicode 字串的一部分 | string substr(string, start [, length]) |
| sum | 計算陣列中值的總和 | number sum(arg1, arg2, ... argN) |
| sumifop | 加總清單中符合條件(arg op value)的已回答問題的值 | number sumifop(op, value, arg1, arg2, ... argN) |
| tan | 正切 | number tan(arg) |
| time | 傳回現在的 UNIX 時間戳記 | number time() |
| trim | 去除字串開頭和結尾的空白(或其他字元) | string trim(string [, charlist]) |
| ucwords | 將字串中每個單字的第一個字元轉換為大寫 | string ucwords(string) |
| unique | 若所有非空白的作答結果皆不相同,則回應「是」 | boolean unique(arg1, ..., argN) |
<span id="Create_new_expression_functions_with_plugin_ (New in 4.0.0 )">
使用外掛程式建立新的運算式函式 (New in 4.0.0 )
如果您需要核心中不存在的新函式,可以使用外掛程式來建立。此類新函式是透過 expressionManagerStart 事件建立的。
ExpressionScript 知道哪些變數是區域變數
為了正確建立頁面的 JavaScript,EM 需要知道哪些變數設定在該頁面上,以及它們的 JavaScript ID 為何(例如用於 document.getElementById(x))。EM 還必須知道哪些變數設定在其他頁面上(以確保所需的 <input type='hidden' value='x'> 欄位存在並已填入值)。
串接條件
只要其中任一變數不相關,整個等式就會變成不相關(false)。例如,在下表中,N/A 表示其中一個變數不相關:
| 運算子 | 範例 | a | b | 結果 |
|---|---|---|---|---|
| + (unary) | +a | N/A | false | |
| ! | !a | N/A | false | |
| == (or eq) | a == b | N/A | 5 | false |
| == (or eq) | a == b | N/A | 0 | false |
| == (or eq) | a == b | N/A | N/A | false |
| != (or ne) | a != b | N/A | 5 | false |
| != (or ne) | a != b | N/A | N/A | false |
| != (or ne) | a != b | N/A | 0 | false |
| > (or gt) | a > b | N/A | 5 | false |
| >= (or ge) | a >= b | N/A | 5 | false |
| < (or lt) | a < b | N/A | 5 | false |
| <= (or le) | a <= b | N/A | 5 | false |
| and | a and b | N/A | 5 | false |
| and | a and b | N/A | N/A | false |
| or | a or b | N/A | N/A | false |
| or | a or b | N/A | 5 | false |
| + | a + b | N/A | 5 | false |
| * | a * b | N/A | 5 | false |
| / | a / b | 5 | N/A | false |
| () | (a) | N/A | false | |
| (exp) | (a && b) | N/A | 5 | false |
| (exp) op (exp) | (b + b) > (a && b) | N/A | 5 | false |
| function | sum(a,b,b) | N/A | 5 | false |
| function | max(a,b) | N/A | 5 | false |
| function | min(a,b) | N/A | 5 | false |
| function | implode(', ',a,b,a,b) | N/A | 5 | false |
| function | if(a,a,b) | N/A | 5 | false |
| function | is_empty(a) | N/A | false | |
| function | is_empty(a) | 0 (or blank) | true | |
| function | !is_empty(a) | N/A | false |
覆寫串接條件
假設您想顯示所有相關答案的累計總和,可能會嘗試使用等式 {sum(q1,q2,q3,...,qN)}。然而,系統內部會將其轉換為 LEMif(LEManyNA("q1","q2","q3",...,"qN"),"",sum(LEMval("q1"),LEMval("q2"),LEMval("q3"),...,LEMval("qN")))。因此,只要 q1-qN 中任一值不相關,該等式就一律回傳 false。在這種情況下,sum() 在所有問題都作答之前都會顯示「0」。
為了解決此問題,可以在每個變數後加上「.NAOK」後綴(表示 Not Applicable is OK,即「不適用也沒關係」)。此時會產生以下行為。假設您有一個變數 q1.NAOK:
- q1 不會被加入 LEManyNA() 子句
- LEMval('q1') 仍會檢查該作答結果是否相關,若不相關則回傳 ""(因此個別不相關的作答結果會被忽略,但不會使整個運算式失效)。
因此,累計總和問題的解決方式是使用等式 sum(q1.NAOK,q2.NAOK,q3.NAOK,...,qN.NAOK)。
使用 .NAOK 後綴也能讓作者設計出具有多條可能路徑、之後再匯合到共同路徑的問卷。例如,假設受訪者的作答超出正常範圍。作者可以提醒受訪者可能無法取得有效的結果,並詢問他們是否真的要繼續填寫問卷。若他們回答「是」,則會顯示其餘的問題。「其餘問題」的條件會檢查最初的作答結果是否在正常範圍內,或者受訪者是否對那個僅在作答超出範圍時才相關的問題回答了「是」。
ExpressionScript 如何支援條件式微調客製化?
以下是微調客製化的範例(其中 Question Type=='expr' 表示等式):
| 問題編號 | 相關性 | 問題類型 | 問題 |
|---|---|---|---|
| name | 1 | text | What is your name? |
| age | 1 | text | How old are you? |
| badage | !is_empty(age) | expr | {(age<16) or (age>80)} |
| agestop | badage | message | Sorry, {name}, you are too {if( (age<16),'young',if( (age>80),'old','middle-aged') ) } for this test. |
| kids | !badage | yesno | Do you have children? |
| parents | 1 | expr | {!badage && kids=='Y'} |
| numKids | parents | text | How many children do you have? |
| kid1 | parents && numKids >= 1 | text | How old is your first child? |
| kid2 | parents && numKids >= 2 | text | How old is your second child? |
| kid3 | parents && numKids >= 3 | text | How old is your third child? |
| kid4 | parents && numKids >= 4 | text | How old is your fourth child? |
| kid5 | parents && numKids >= 5 | text | How old is your fifth child? |
| sumage | 1 | expr | {sum(kid1.NAOK,kid2.NAOK,kid3.NAOK,kid4.NAOK,kid5.NAOK)} |
| report | parents | text | {name}, you said you are {age} and that you have {numKids}. The sum of ages of your first {min(numKids,5)} kids is {sumage} |
若要下載上述問卷範例,請點選以下連結:子女人數問卷範例。
所有這些問題都可以放在同一頁(例如在同一個題組中),而且只會顯示相關的問題。此外,當您輸入子女的年齡時,最後一個問題中的 sum() 運算式會動態更新。
ExpressionScript 透過以具名的 <span> 元素包住每個運算式來提供此功能。每當數值變更時,它會重新計算應顯示於該 <span> 元素中的運算式,並重新產生顯示內容。同一頁面上可以有數十甚至數百個這類客製化運算式。
語法醒目提示
為了協助輸入與驗證運算式,EM 提供具有下列功能的語法醒目提示:
語法醒目提示的類型與意義
| 顏色 | 範例 | 意義 | 提示文字 | 備註 |
|---|---|---|---|---|
| 淺褐色背景 | Sample | 整個等式 | 無 | 大括號內任何被辨識為等式的內容(例如前後沒有空白字元)都會以淺褐色背景標示,以便與周圍的文字區分。 |
| 紅色粗體文字 | Sample | 錯誤 | 對錯誤的一些說明 | 可能是未知的變數或函數中的錯誤。問卷已損壞,依賴該運算式的問題將不會顯示給受訪者。 |
| 藍色文字 | Sample | 函數名稱 | 意義與允許的語法 | 指函數名稱,或因後面接著左括號而應為函數的內容,以藍色粗體文字顯示。提示文字會顯示該函數的意義與允許的語法。 |
| 灰色文字 | Sample | 字串 | 無 | 單引號與雙引號字串以灰色文字顯示。 |
| 青色文字 | Sample | 設定在同一頁的變數, | [name]: question; value; answerList showing codes for each value | 設定在您目前編輯之問題所在同一頁的任何變數,都以青色文字顯示(可在 JavaScript 中更新)。提示文字會顯示其名稱、實際問題及現在值(未設定則為空白)。若問題類型預期的作答結果來自一組列舉值,則會顯示代碼與顯示值之間的對應。 |
| 綠色文字 | Sample | 設定在前面頁面的變數 | [name]: question; value; answerList showing codes for each value | 設定在前面頁面的任何變數,都以綠色粗體文字顯示。提示文字會顯示其名稱、實際問題及現在值(未設定則為空白)。若問題類型預期的作答結果來自一組列舉值,則會顯示代碼與顯示值之間的對應。 |
| 粉紅色粗體文字 | Sample | 設定在後面頁面的變數 一般而言:問卷開始時為空,但可透過索引或返回上一頁來填入 | [name or SGQA code]: question; value; answerList showing codes for each value | 設定在後面頁面的任何變數,都以粉紅色粗體文字顯示。提示文字會顯示其名稱、實際問題及現在值(未設定則為空白)。若問題類型預期的作答結果來自一組列舉值,則會顯示代碼與顯示值之間的對應。 |
| 褐色粗體文字 | Sample | LimeSurvey 取代值 | 該值 | LimeSurvey 取代字串(如 {TOKEN:xxx}、{PRIVACY_MESSAGE})以褐色粗體文字顯示。 |
| 紅色文字 | Sample | 指派運算子 (=) | 警告訊息 | 如果您使用指派運算子 (=),該運算子會以紅色文字顯示。這是為了避免在您其實想檢查 a == b 時,意外變成設定 a = b 而重新指派數值。 |
| 一般黑色文字 | Sample | 標點符號 | 無 | 運算式中所有其他標點符號以一般黑色文字顯示。 |
| 紅框文字 | 錯誤周圍有紅色粗線框 | 語法錯誤 | 錯誤說明 | 偵測到的任何語法錯誤都會顯示在紅框內。提示文字會顯示該錯誤。例如括號未成對、使用未定義的函數、傳給函數的引數數量錯誤、運算式結構不良(例如變數之間缺少運算子)、嘗試對唯讀變數指派新值、嘗試對非變數指派值,或使用未支援語法。請注意,即使運算式中有多個錯誤,語法錯誤偵測系統可能只會回報一個錯誤;不過,只要偵測到錯誤,至少會顯示一個。 |
延伸閱讀
ExpressionScript 範例問卷
使用案例與操作指南
逐步範例
開發人員參考資料