運算式管理員
From LimeSurvey Manual
快速入門教學
概觀
自訂問卷時,您通常需要一種方式來指定下列事項:
- 導覽/分支 - 讓受訪者的答案改變問題出現的順序
- 客製化/管線傳遞(Tailoring/Piping) - 決定問題的措辭方式(例如引用先前的答案,或根據受訪者的人數或性別調整句子),或產生自訂報告(例如評估分數或量身打造的建議)。
- 驗證 - 確保答案符合特定條件,例如最小值與最大值,或符合輸入格式
運算式管理員(Expression Manager,簡稱 EM)提供一種直覺的方式來指定上述各項功能的邏輯。幾乎所有可以寫成標準數學等式的內容都是有效的運算式,即使您呼叫了函數也一樣。EM 目前提供 80 個函數,並且可以輕鬆擴充以支援更多函數。它也讓您可以使用易於閱讀的變數名稱(而非 SGQA 名稱)來存取變數。
以下各節說明運算式管理員的主要使用位置
相關性(控制導覽/分支)
有些問卷使用「Goto 邏輯」,例如當您以選項 C 回答問題 1 時,就跳至問題 5。這種方式限制很多,因為難以驗證,而且在重新排序問題時很容易出錯。EM 使用布林值的相關性等式,來指定問題何時有效的所有條件。如果問題具有相關性,就會顯示該問題;否則視為不適用,並在資料庫中儲存 NULL 值。這與透過條件編輯器所能達成的功能類似,但 EM 讓您可以輕鬆指定更複雜、更強大的條件(並且可以使用變數名稱而非 SGQA 命名)。
此圖顯示檢視問卷相關性邏輯的其中一種方式。它計算身體質量指數(BMI)。相關性等式顯示在變數名稱(綠色)後方的方括號中。因此,weight、weight_units、height 與 height_units 的相關性皆為 1,表示這些問題一律會被詢問。然而,BMI 的相關性為 {!is_empty(height) and !is_empty(weight)},表示只有在受訪者同時輸入身高與體重的值時才會計算 BMI(藉此避免除以零的錯誤)。此外,只有在受訪者回答全部四個主要問題(height、height_units、weight、weight_units)時,才會顯示 Report 問題。
相關性會顯示並可在下列位置編輯:
檢視/編輯問題層級的相關性
此等式計算身體質量指數(BMI)。只有在受訪者先輸入身高與體重時才會詢問此問題。
這是 BMI 問題的編輯畫面。
請注意,輸入相關性等式時不要使用大括號。
檢視/編輯群組層級的相關性
這是一份人口普查問卷範例。第一頁詢問有多少人與您同住,並將結果儲存在「cohabs」變數中。只有在同住者超過一人時才會顯示此頁(因此會針對第二位同住者顯示),而且只有在您指定了第一位人員與您的關係(p1_rel)時才會顯示。
如您所見,該群組也具有問題層級的相關性條件,因此每個問題只有在您回答了它前一個問題後才會出現(例如 {!is_empty(p1_sex)})。EM 會為您合併群組與問題層級的相關性。只有在整個群組具有相關性時,才會詢問群組中的問題。接著,只會詢問群組中具有相關性的那部分問題。
以下是編輯該問題的群組層級相關性的畫面:
請注意,輸入相關性等式時不要使用大括號。
客製化/管線傳遞(Tailoring/Piping)
EM 讓您可以輕鬆對問題進行簡單與複雜的條件式客製化。有時您只需要簡單的替換,例如說「您表示購買了[產品]。您最喜歡它的哪一點?」。有時您需要條件式替換,例如「[先生/女士] [姓氏],您願意完成我們的問卷嗎?」。在此情況下,您希望根據對方的性別使用「先生」或「女士」。其他時候您需要更複雜的替換(例如根據數學運算)。EM 支援上述各種客製化/管線傳遞。
條件式等式
身體質量指數範例展示了計算個人 BMI 的能力,即使讓受訪者以公制或非公制單位輸入身高與體重也一樣。
這裡的 weight_kg 為 {if(weight_units == 'kg', weight, weight * .453592)}。此 if() 函數的意思是:如果受訪者以公斤輸入體重,就直接使用該值;否則將輸入值(以磅為單位)乘以 .453592,轉換為公斤。height_m 變數採用類似的方法計算受訪者的身高(以公尺為單位),即使他輸入的是英寸。
BMI 的計算公式為 {weight_kg / (height_m * height_m)}。
最後,報告會依條件為受訪者量身訂製訊息,告知他所輸入的內容。(「您說您身高 2 公尺,體重 70 公斤。」)
雖然上圖未能清楚顯示,weight_status 使用巢狀 if() 陳述式,將受訪者分類為體重過輕到重度肥胖。您可以在 Show Logic View 中查看其等式。
從此問題的編輯視窗中,您可以看到兩件事:
- 量身訂製(Tailoring)必須以大括號包住運算式
- 運算式可以跨越多行,如本例所示,這樣可以讓巢狀條件邏輯更易於閱讀。
量身訂製的問題、答案與報告
此範例顯示 BMI 報告。
以下是同一問題的編輯視窗。
如您所見,大括號內的任何內容都會被視為運算式,因此在上一張圖中會以語法醒目提示(顏色標示)。如果出現任何打字錯誤(例如變數或函數名稱拼錯或未定義),EM 會顯示錯誤,如下圖所示:height_unit 是未定義變數(正確名稱為 height_units),rnd() 是未定義函數(正確的函數名稱為 round())。在這兩種情況下,錯誤都會以紅色方框框起來,方便您發現並修正。
您還可以看到,您能快速建立複雜的報告,例如輸入值的表格或量身訂製的建議。
請記住,所有量身訂製都必須以大括號包住運算式,這樣 LimeSurvey 才能知道問題中哪些部分是自由文字,哪些部分應交由 Expression Manager 解析。
驗證
EM 控制大多數進階問題選項的運作方式。這些選項涵蓋答案數量的最小/最大值、單一數值的最小/最大值、數值總和的最小/最大值,以及檢查輸入值是否符合指定的字串模式。您仍然照常輸入這些進階問題選項。不過,現在這些欄位中的任何值都會被視為運算式,因此您可以設定與其他問題具有複雜條件關係的最小/最大值條件。
在所有這些情況下,由於進階問題選項一律被視為運算式,因此在指定時不需使用大括號。
範例問卷頁面顯示了許多使用運算式進行驗證的實際範例。
簡介
LimeSurvey 使用全新的 Expression Manager(EM)模組,使 LimeSurvey 能支援更複雜的分支、評估、驗證和量身訂製。它將取代 LimeSurvey 在後端管理替換欄位(Replacements)、條件(Conditions)和評估(Assessments)的方式。由於幾乎消除了執行時的資料庫讀取,它也將大幅加快處理速度。EM 由 Thomas White 博士(TMSWhite)開發。
本 Wiki 頁面是 Expression Manager 語法與功能的權威參考資料。
主要定義
- 運算式:以大括號包住的任何內容
- 前提是左大括號之後或右大括號之前不得緊接空白字元
- 運算式的內容由 EM 評估,因此可以包含數學公式、函數,以及複雜的字串和日期處理。
- 量身訂製(Tailoring):有時稱為「管線傳遞(piping)」,是依條件修改文字的過程
- 您可以存取所有「替換欄位」、參與者資料和作答資料。
- 您也可以更輕鬆地存取問題、答案及其屬性。
- 相關性等式:控制問題可見性的全新問題屬性
- 如果有相關性等式,則只有在相關性評估為 true 時才會顯示該問題。
- 在內部,所有 array_filter 和 array_filter_exclude 命令都會變成子問題層級的相關性
- 公式題型:將計算結果或報告儲存到資料庫的全新題型
- 它類似於 Boilerplate 問題,但即使您設定了「始終隱藏本題」,其內容仍會儲存到資料庫
- 問題編號:這是 EM 偏好使用的變數名稱
- 它可以是說明問題用途的描述性名稱,使複雜的邏輯更易於閱讀
- 有效的問題編號不應以數字開頭,因此若使用問題編號為問題編號碼,請直接使用「q1」、「q1a」或「g1q2」。
- 如果您將資料匯出至 SPSS 或 R,這就是目前的變數名稱,因此如果您進行統計分析,可能已經將其設為唯一。
我必須使用 EM 嗎?
簡短的回答是:不必(但也可以說是需要)。
EM 完全向下相容現有問卷。因此,如果您習慣使用 LimeSurvey 1.91+ 及更早版本中的條件(Conditions)和評估(Assessments)方式,您可以繼續使用。
不過,EM 完全取代了 LimeSurvey 內部處理條件(Conditions)的方式。雖然您仍可使用條件編輯器來建立和管理條件,但 LimeSurvey 1.92 會將這些條件轉換為對應的相關性等式。在升級過程中,LimeSurvey 1.92 會將所有現有條件自動轉換為相關性等式。
這應能讓您兩全其美:您可以繼續以習慣的方式使用 LimeSurvey,同時也能看到對應的相關性等式,以便在適當的時候逐步直接改用相關性等式。
我可以混用條件與相關性嗎?
可以。您可以對部分問題使用條件編輯器,對其他問題使用相關性編輯器。當您儲存問題時,條件會自動轉換為相關性。
請注意,如果您使用條件編輯器,我們會假設您希望這些條件覆寫任何手動輸入的相關性等式。因此,如果您已有條件,並想手動編輯相關性,請先刪除該問題的條件。具體做法是:先將產生的相關性等式複製到文字編輯器,使用「條件」選單刪除該問題的所有條件(這也會刪除相關性),然後編輯該問題,並將文字編輯器中產生的相關性等式貼回該問題的相關性欄位(並儲存問題)。如果有足夠多的使用者需要在不刪除所產生相關性等式的情況下刪除條件,我們可以新增批次轉換流程。
我應該如何在條件與相關性之間做選擇?
以下列出兩種方式的優缺點:
| 方式 | 優點 | 缺點 |
|---|---|---|
| 條件 | 1. 具備方便建立簡單條件的圖形化介面 2. 圖形化介面有完善的文件,支援團隊也很熟悉 |
1. 僅支援簡單的比較,且無法妥善處理 AND/OR 條件 2. 串接條件的運作不穩定 3. 速度慢 - 大量存取資料庫,可能拖慢較長的問卷 4. 有回報重新載入條件時發生問題 5. 當問題多達數十、數百或數千個時,圖形化介面難以擴充 6. 由於必須使用 SGQA 名稱,轉換紙本問卷可能較慢 7. 複雜的分支邏輯通常需要程式設計師自訂程式碼 |
| 相關性 | 1. 支援非常複雜的邏輯,包括 80 多個函數以及數學/字串運算子 2. 完美支援串接邏輯 3. 速度快 - 不需額外存取資料庫,因此支援 1000 個以上問題的問卷 4. 由於不需要 SGQA 代碼,重新載入邏輯不會有問題 5. 語法醒目提示可擴充至 1000 個以上問題的問卷 6. 適合想將現有紙本問卷電腦化的團體,使用簡單快速 7. 無需程式設計師,即可輕鬆支援半結構式訪談與流行病學問卷 |
1. 簡單條件沒有圖形化介面 - 請改用語法醒目提示 2. 屬於新功能,支援團隊尚未完全掌握 EM。 |
總而言之,如果您對 LimeSurvey 1.91+ 的運作方式感到滿意,就沒有理由改變您目前的做法。
使用 EM 還有哪些其他好處?
以下是您可能想使用 EM 的其他原因。
- 計算 - 您可以建立任何您想得到的計算:
- 您可以使用所有常見的數學運算子與函數
- 您可以使用 70 多個數學、日期及字串處理函數
- 如果使用者有需要,開發人員可以相當輕鬆地新增函數
- 將計算結果儲存至資料庫
- 現在您可以計算簡單與複雜的運算和/或量表分數,並將其儲存至資料庫,而不需要 JavaScript。
- 您可使用等式題型來達成此目的。
- 評估
- 現在您可以從任何題型建立評估或量表分數,而不僅限於過去支援的部分題型
- 您可以使用個人化(Tailoring)在任何需要的地方顯示累計或總評估分數 - 甚至在同一頁面上
- 您可以更有彈性地控制根據這些評估分數所產生的報告
- 您可以將評估分數儲存至資料庫,而不需要 JavaScript
- 您可以隱藏評估分數,而不需要 JavaScript 或 CSS
- 取代欄位
- 使用問題編號插入先前問題答案的值 - 這讓內容更容易閱讀與驗證。
- 個人化(Tailoring)- 您可以根據其他值有條件地顯示文字
- 為受訪者使用適當的稱謂,例如(如:「您好,[先生/女士] 王」)
- 在單數/複數有差異時,輸出文法正確的句子:(如:「You have 1 child」與「You have 2 children」)
- 根據受訪者的性別與單複數,適當地變化動詞與名詞。
- 新的變數屬性 - 您可以使用下列屬性來進行個人化:
- (無後綴) - qcode.code 的別名
- .code - 若該問題相關,則為所選的作答代碼(否則為空白);若不是編碼問題,則為文字值
- .NAOK - 與 .code 相同,但即使不相關,也可用於計算或清單中
- .value - 若該問題相關,則為該問題的評估值(否則為空白);若不是編碼問題,則為文字值 -- 僅在問卷啟用評估時可用,否則一律為零
- .valueNAOK - 與 .value 相同,但即使不相關,也可用於計算或清單中
- .shown - 顯示給使用者的答案
- .qid - 問題 ID
- .gid - 群組 ID
- .sgqa - 該問題的 SGQA 值
- .jsName - 該問題正確的 JavaScript 變數名稱,無論是在本頁或其他頁面定義
- .qseq - 問題順序(從 0 開始)
- .gseq - 群組順序(從 0 開始)
- .mandatory - 該問題是否為必填(Y/N)
- .question - 問題的文字
- .relevance - 該問題的相關性等式
- .grelevance - 該群組的相關性等式
- .relevanceStatus - 該問題目前是否相關(布林值(在 PHP 與 JS 中可能不同))
- .type - 題型(單一字元代碼)
- 頁面內動態變更
- 所有相關性、計算與個人化都會在頁面上動態運作 - 因此數值變更時頁面會立即更新
- 因此,問題會根據是否相關而動態顯示/隱藏
- 問題也會根據頁面上的作答結果動態個人化,讓您看到累計總數、個人化句子與自訂報告。
- 新的資料輸入頁面
- 除了使用現有的資料登錄系統外,您也可以直接使用 Survey-All-In-One。
- 這支援頁面內的相關性與個人化,因此資料登錄人員可以快速以 Tab 鍵切換,且只需輸入相關的作答結果
- 如果您的資料登錄人員需要看到同樣為動態的個人化內容,這一點可能至關重要。
- 免除大部分自訂 JavaScript 的需求
- EM 可輕鬆支援複雜的計算、計分、個人化與條件邏輯。
- 某些情況仍需要 JavaScript(例如自訂版面配置及有條件地隱藏問題的子元素),但您的 JavaScript 可以使用 EM 函數,讓您能以 Qcode 而非 SGQA 存取問題,並存取上述列出的任何問題屬性。
EM 還啟用了哪些其他實用的新功能?
無論您是繼續使用條件編輯器,還是手動撰寫相關性等式,都可以獲得以下額外好處:
- 您可以建立更複雜的驗證條件
- 所有進階問題屬性(例如 max_answers、min_num_value_n、max_num_value)都可以使用運算式。因此,即使在同一頁面上,您也可以根據先前的作答結果輕鬆調整最小值/最大值條件。
- EM 也處理所有以正規表示式為基礎的驗證,因此您可以穩健地結合 preg 與以等式為基礎的問題屬性。
- 輕鬆重新排序(或刪除)問題與群組
- 在 1.92 版之前,若 LimeSurvey 認為重新排序可能會破壞問題或群組所使用的條件,您就無法重新排序。同樣地,若有其他問題依賴某個問題,您也無法刪除該問題。
- 藉由 EM 的語法醒目提示,您可以輕鬆查看並驗證是否在宣告之前就使用了問題。因此,現在您可以隨時重新排序或刪除問題與群組。EM 會更新所有語法醒目提示,以顯示潛在的錯誤。
- 重新排序問題的檢視畫面已經強化,以協助進行此類檢查。現在它會顯示問題的相關性等式與個人化內容,讓您可以立即看出是否有任何變數變成粉紅色(表示在宣告之前就被使用)。
- 問題/群組導覽索引始終可用且正確
- 在 1.92 版之前,若有複雜的條件,這些索引就無法使用
- 使用 EM,我們可以保證這些索引是正確的。
- 受訪者甚至可以跳回先前的問題、變更答案,然後再往前跳(或送出)
- 往前跳時,EM 會重新驗證中間所有的問題/群組。
- 若有任何問題變得不相關,它們在資料庫中會被設為 NULL,以確保您的資料內部一致
- 若有任何問題變得相關,或新近未通過必填或驗證規則,EM 會停留在該頁面,並強制使用者先回答這些問題,才能跳到最終目的地。
- 將條件自動轉換為相關性
- 升級資料庫時,所有含有條件的現有問卷都會產生對應的相關性等式
- 每當您匯入問卷時,會視需要建立相關性等式
- 每當您新增、刪除或修改條件時,EM 都會產生適當的相關性等式。
- 方便的語法醒目提示
- 當 EM 顯示相關性等式時,即使您輸入的是 SGQA 代碼,它也會顯示 Qcode,因為我們認為這樣比較容易閱讀。
- 所有變數都以顏色標示,以顯示它們是在目前問題(或目前群組)之前還是之後宣告。這讓您可以快速發現並修正在宣告之前就將變數用於相關性(包括 array_filter)、個人化或驗證等式的情況。
- 此外,如果您將滑鼠游標停在以顏色標示的變數上,就會看到該問題最重要的中繼資料。其中包括群組順序編號、問題順序編號、Qcode、問題文字,以及所有可用的答案選項(若該題型具有列舉的答案選項)。
- 答案選項清單使用以下語法:'answers':{key:val, ... }。
- key 的語法為 'scale~code',其中 scale 是答案分級(例如用於雙重分級),code 是答案代碼。
- val 的語法為 'value~shown',其中 value 是評估值(若使用評估,否則為 code)(例如 Qcode.value),shown 是受訪者所看到的顯示值(例如 Qcode.shown)。
- 這表示許多問卷無需評估模式即可使用計算。如果您的列舉答案選項是唯一、非小數且非負數,您就可以直接對 Qcode.code 值進行計算。
- 輕鬆檢閱整份問卷的邏輯與內容
- 新增了顯示問卷邏輯功能,讓您可以在單一頁面上查看問卷(或群組或問題)的所有內容。
- 它會顯示所選範圍(問卷、群組或問題)的群組、問題、子問題與答案層級的詳細資訊。
- 它也會顯示相關性、子問題層級的相關性(用於 array_filter 與 array_filter_exclude)、產生的驗證等式(用於 preg 以及任何驗證規則,例如最小/最大總和/值的數量),以及所有非空白的問題屬性。
- 所有內容都經過語法醒目提示,讓您可以看出潛在的語法錯誤(例如括號不成對,或在宣告之前就使用變數)
- 語法醒目提示有助於快速瀏覽與編輯問卷。
- 如果您按一下變數名稱,會開啟一個瀏覽器視窗(或分頁),顯示該問題並讓您編輯。
- 如果您按一下群組名稱,會開啟一個瀏覽器視窗(或分頁),顯示群組重新排序檢視,讓您可以輕鬆移動問題。
- 所有問題屬性也都經過語法醒目提示。這讓您可以在進階問題選項中設定並查看運算式(例如以運算式作為最大/最小值數量/總和的依據)
- EM 的作者使用類似的檢視(稍微更簡潔),讓與他合作的流行病學家與機構審查委員會,驗證並核准含有數千個問題、具高度分支與個人化結構的訪談式問卷
開始使用
開始使用 EM 的最佳方式如下:
- 從 http://www.limesurvey.org/en/download 安裝最新的穩定版本
- 匯入並探索範例問卷。
- 探索使用案例與操作指南以及逐步範例。
- 探索 EM 文件(本頁面)
- 檢視內建的 EM 測試套件
- 在任何問卷中,於「工具」下選擇 EM 選項
- 可用功能列出 70 多個函數及其語法
- 獨立運算式的單元測試
- 顯示使用所有 EM 函數與運算子的範例,以及 PHP 與 JavaScript 的結果
- 請注意,有少數函數在 PHP 與 JavaScript 版本中會產生不同的結果,因此您可以透過此頁面規劃您的 EM 邏輯。
Expression Manager 擴充/取代了哪些功能?(LimeSurvey <= 1.91+)
條件 => 相關性
條件用來控制哪些問題可見。一般語法為 SGQA 運算子 值,例如 111X2X3 == "Y"。條件可以使用 AND 或 OR 組合,但混合 AND 與 OR 則很困難。條件本身儲存在獨立的資料表中,LimeSurvey 有大部分程式碼都用於管理條件。由於需要大量存取資料庫,處理大量條件可能會造成明顯的效能問題。此外,一旦將條件指派給問題或群組,您通常就不能重新排序或刪除它們。
評估 => 等式與微型個人化
評估讓使用者能夠從一組問題建立分級分數。然而,它們無法在目前頁面上動態變更,且其值不會儲存到資料庫。
取代 => 微型個人化
使用者可以根據先前的作答結果,對部分訊息與問題進行個人化。例如,某個問題可能是 {TOKEN:FIRSTNAME},您表示 {SPORT.shown} 是您最喜愛的運動。然而,若不使用複雜的 JavaScript,就無法進行條件式個人化(例如依據對方的性別稱呼「先生」或「女士」),也無法變化動詞或名詞。作者雖然可以製作看似已個人化問題的問卷,但每種排列組合都需要各自的問題,並且需要複雜的條件來決定要顯示哪些問題。
驗證
問題可以使用正規表示式、最小值/最大值來驗證,或讓某個作答結果作為最小值或最大值。然而,若不使用複雜的 JavaScript,驗證無法以其他變數的計算結果為依據。
等式
若不使用複雜的 JavaScript,就不支援等式。
等式問題類型
若不使用複雜的 JavaScript,等式(例如評估的最終分數)就無法儲存到資料庫。
Expression Manager 將如何取代/擴充該功能?
Expression Manager 是 LimeSurvey 中新的核心模組,讓原本需要自訂 JavaScript 的複雜功能更容易實現。它也正在取代 LimeSurvey 目前管理條件與評估的方式。
提及 Expression Manager (EM) 時的新術語
EM 以下列術語來「看待」其功能:
- 以相關性為基礎的分支 - 如果問題相關,就提出該問題,否則不提出(例如讓它不可見,並在資料庫中標記為 NULL)。所有問題類型以及每個群組都新增了相關性欄位(因此您可以將一組條件套用到整個群組,而不必將相同的條件複製到每個問題,及/或結合群組與問題層級的條件邏輯)。
- 個人化 - 一旦知道應該提出哪些問題,個人化(有時稱為 piping)就會指定該如何提出問題。這不僅支援簡單的替換(例如 {TOKEN:FIRSTNAME}),也支援依據受訪者的性別或數量來變化動詞與名詞。它還能讓您根據受訪者是否回答(或如何回答)其他問題,來變更傳達給他們的訊息。
- 等式 - EM 新增了一種稱為「等式」的問題類型,用來儲存運算式的結果。這些等式的結果會被計算並寫入資料庫,即使您在頁面上將其隱藏也一樣。因此,它們適用於隱藏的計分計算、依據複雜等式進行的導覽、評估,以及應產生並可在資料庫中輕鬆取得的報告。
相關性與串聯相關性
現在每種問題類型都有一個相關性選項,用來控制是否顯示該問題。EM 會依照問題在問卷中應出現的順序,處理每一個相關性等式。如果運算式為真(或缺少運算式 - 以支援舊版問卷),就會顯示該問題。如果不相關,則該問題會被隱藏,且其值在資料庫中會被設為 NULL。如果群組中沒有任何相關的問題,則整個群組都會被略過。
此外,如果運算式中任何一個變數不相關,則該運算式一律會評估為假。這啟用了串聯相關性,因此您不必為每個問題撰寫非常冗長的相關性等式。
假設您有 5 個問題 Q1-Q5,並且只希望在 Q1 已作答時才顯示 Q2,在 Q2 已作答時才顯示 Q3,依此類推。相關性等式可能如下:
| 問題編號 | 相關性 | 問題 |
|---|---|---|
| Q1 | 1 | 您的名字是什麼? |
| Q2 | Q1 | {Q1},您今年幾歲? |
| Q3 | Q2 | 所以,您今年 {Q2} 歲。您已婚嗎? |
| Q4 | Q3 == "Y" | {Q1},您結婚多久了? |
| Q5 | Q4 | {Q1},您有幾個孩子? |
相關性計算在 JavaScript 中同樣有效 - 因此您可以將上述所有問題放在同一頁面上,仍然會如預期運作。事實上,EM 完全取代了處理逐問卷、逐群組與逐題問卷格式的方式。它們現在都使用完全相同的導覽引擎,因此無論問卷樣式為何,運作方式都相同。
只要您仍在同一頁面上,您輸入的任何資料仍會保留,只是被隱藏。因此,如果您輸入了一些資訊,然後選擇了使其不相關的選項,接著又使其再次相關,您的答案仍然可用。然而,一旦您移至不同的頁面,所有不相關的作答結果都會遺失,以確保資料集的完整性。
群組層級相關性
Expression Manager 也支援群組層級的相關性,讓您更容易實作迴圈。假設您想收集最多 10 個實體(例如產品或家庭成員)的資訊,且您會先確定有多少實體需要後續追問(例如詢問家中有多少人同住,或讓受訪者從長清單中勾選喜歡的產品)。得知需要後續追問的實體數量後,您可以為 10 組後續追問的問題群組分別使用群組層級的相關性,例如 {count >= 1}、{count >=2}、... {count >= 10}。在每個群組內,您還可以設定問題層級的條件邏輯(例如針對每位受訪者的性別或年齡的後續問題)。問題與群組層級的相關性運算式會以 AND 結合,以決定要顯示哪些內容。
量身訂製/管線傳遞(Tailoring / Piping)
大括號內的任何內容現在都會被視為運算式(有一項例外,說明如下)。運算式可以存取所有 LimeReplacementFields、所有變數(透過數個別名)、所有常見的運算子(數學、邏輯和比較),以及數十種函數(這些函數甚至能在用戶端動態運作)。
使用這些運算式,您可以執行以下操作:
- 根據先前的作答結果,有條件地向受訪者顯示量身訂製的訊息
- 建立評估並顯示評估結果(或根據這些結果有條件地分支或顯示訊息),完全不需使用評估模組本身
- 在問題、答案和報告中變化動詞和名詞的詞形。
- 在問卷結尾的「顯示您的答案」頁面之前,顯示作答結果摘要
運算式(Equations)
新增了一種稱為 Equation 的問題類型。它類似 Boilerplate 問題,不同之處在於它會將顯示的內容值儲存到資料庫中。因此,如果 Equation 問題的問題文字包含評估計算,該值就會儲存在資料庫的變數中,並可顯示於公開或私人統計中。
這解決了將評估分數儲存到資料庫中這個常見的需求。
語法
大括號內的任何內容現在都被視為運算式(有一項例外:開頭和結尾都不能有空白字元,這是為了確保 Expression Manager 不會嘗試處理內嵌的 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',
''
)
)
)
)
)
)
)
)}
- 所有標準數學運算子(例如 +,-,*,/,!)
- 所有標準比較運算子(例如 <,<=,==,!=,>,>=,以及這些等效寫法:lt,le,eq,ne,gt,ge)
- 括號(可用來將子運算式分組)
- 條件運算子(例如 &&,| | 以及這些等效寫法:and,or)
- 單引號與雙引號字串(兩者可互相內嵌另一種引號的字串)
- 逗號運算子(可以列出多個運算式,並只傳回最後的結果)
- 指派運算子(=)
- 預先定義的變數(用來參照問題、問題屬性和作答結果),例如所有的 SGQA 代碼
- 預先定義的函數(目前已有 70 多個,而且很容易新增更多)
運算子
EM 語法遵循一般的運算子優先順序:
| 層級 | 運算子 | 說明 |
|---|---|---|
| 1 | () | 用於分組或呼叫函數的括號 |
| 2 | ! - + | 一元運算子:非、負號、正號 |
| 3 | * / | 乘、除 |
| 4 | + - | 加、減 |
| 5 | < <= > >= lt le gt ge | 大小比較 |
| 6 | == != eq ne | 相等比較 |
| 7 | and | 邏輯 AND |
| 8 | or | 邏輯 OR |
| 9 | = | 指派運算子 |
| 10 | , | 逗號運算子 |
加號運算子 (+) 的注意事項
為了讓 JavaScript 與 PHP 保持一致,如果兩個運算元都是數字,加號運算子 (+) 會進行加法運算;如果兩者都是非數字的字串,則會進行串接。不過,我們建議使用 join() 函數進行串接、使用 sum() 函數進行加法運算,這樣能讓您的意圖更清楚,並避免在預期是字串卻得到數字(或相反)時產生非預期的結果。
數字與字串不一致及字母順序比較的注意事項
當您要以大小或相等比較來比較數值時,請留意型別不一致的問題。如果使用者輸入的值或所選的答案代碼明顯是數字,就可以當作數字使用。
如果您在其中一個值周圍加上 ",就會強制以文字方式比較(字母順序比較)。若要以數值方式比較,請不要在數字周圍加上引號 "。
例如,如果 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 卻仍保持已作答的問題,或是被跳過但應該作答的問題)。一般而言,如果您想為變數指派值,應建立一個 Equation 問題類型,並使用運算式來設定其值。不過,偶爾也有人真的需要這個運算子,所以我們仍然提供了它。
為了提醒您留意這個運算子,它在語法運算式中會以紅色字型顯示(以免與「==」混淆)。
使用指派運算子
您可能想使用指派的主要原因如下:
- 您需要為不接受以運算式設定預設值的問題設定預設值(例如單選清單,其使用者介面可讓您選擇其中一個答案選項,但無法輸入運算式)。不過請小心,LimeSurvey 無法驗證您的運算式所產生的結果是否為該問題的允許答案之一。
- 您需要根據後面的作答結果,強制變更先前問題的作答結果
- 您需要複雜的配額:使用一個隱藏的單選問題作為配額,並透過指派來設定它
- 等等...
為此目的,您可以使用整個 Expression Manager 系統。
一些範例:
- 將簡短文本問題的答案設為小寫:
{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 防護後,部分 Expression Manager 功能將無法使用:
- 在一個運算式中開啟 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>
存取變數
Expression Manager 提供對所需任何變數的唯讀存取。為了向下相容,它提供對下列項目的存取:
- TOKEN:xxx - TOKEN 的值(例如 TOKEN:FIRSTNAME、TOKEN:ATTRIBUTE_5)(僅適用於非匿名問卷)。
- QCODE.shown - 使用 Expression Manager 時,答案的顯示值(例如「是」)。
- 範本使用的所有 {XXX} 值。
- 在問題文字中,您可以使用 {QID} 取代為問題 ID,以及使用 {SGQ} 取代為該問題的 SGQA。
此外,Expression Manager 讓您可以透過問題編號(資料庫中問題資料表的「title」欄)來引用變數。這也是您將資料匯出至 SPSS、R 或 SAS 時所使用的變數標籤。例如,如果您有關於姓名、年齡和性別的問題,您可以將這些變數稱為 name、age 和 gender,而不是 12345X13X22、12345X13X23 和 12345X13X24。這讓每個人都更容易閱讀等式並驗證其邏輯,而且無需追蹤群組或問題編號,就能隨意調整問題的順序。
重要: 只能安全地引用出現在前面頁面或問題中的變數。
此外,Expression Manager 讓您可以存取問題的許多屬性:
| 語法 | 意義 | 範例 | 範例結果 |
|---|---|---|---|
| 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 代表 >=
Qcode 變數命名
以下依問題類型說明如何建構 Qcode(以及存取部分屬性)。一般而言,Qcode 的建構方式如下:
QuestionCode . '_' . SubQuestionCode . '_' . ScaleId
是否需要使用子問題代碼與量表 ID,取決於問題類型 - 請參閱下表。 對於 comment 和 other,問題編號為 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'} |
保留變數 'this'、'self' 和 'that'
您經常需要評估問題的所有部分,例如計算已回答的子問題數量,或加總分數。有時您只想處理問題中特定的列或欄(例如取得列或欄的總和並儲存到資料庫)。這些保留變數可讓此過程變得相對輕鬆。
'this' 變數僅用於「整體問題驗證等式」和「子問題驗證等式」這兩個進階問題選項中。它會展開為這些問題中每個儲存格的變數名稱。因此,如果您要確保每個輸入值都大於三,可以將「子問題驗證等式」設定為 (this > 3)。
'self' 和 'that' 變數功能更強大,它們是在處理公式之前先行展開的巨集。可用的語法如下:
- self
- self.後綴
- self.sub-selector
- self.sub-selector.後綴
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 ....
- nosq_X - 其中 X 是列或欄的識別碼。僅選取不符合樣式 X 的子問題。請注意,搜尋是針對完整的代碼識別碼進行,然後 nosq_X 會比對,且不包含子問題 nX、X、Xn
範例:
- 問題的任何部分是否已作答? {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' 變數與 'self' 變數類似,但可讓您引用其他問題。其語法如下:
- that.qname
- that.qname.suffix
- that.qname.sub-selector
- that.qname.sub-selector.suffix
qname 是不含任何子問題擴充的問題名稱。例如,假設您建立了問題 'q1',那麼它的 qname 就是 q1
範例:
- 問題 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' 展開後的值。這可讓您看到實際產生的等式,以便您(以及 Expression Manager)驗證這些變數是否存在。由於您可能會看到相當冗長的等式,這看起來或許令人困惑。不過,如果您編輯該問題,將會看到使用 'self' 和/或 'that' 的原始等式。
另請注意,如果您 (a) 想要明確指定運算式中使用的每個變數,或 (b) 使用沒有子問題的變數(例如單選問題),則不應使用這些變數。在這些情況下,在變數前加上 'that' 前綴是多此一舉,而且可能會得到非預期的結果。
NAOK 的用法
NAOK --> "Not Applicable" (NA) is alright (OK),即「不適用」也沒關係
使用 NAOK,表示所有或部分變數與目前情況無關(即 "Not Applicable" (NA) is alright (OK),「不適用」也沒關係)。
當您在任何運算式中使用某個問題的變數時:如果該問題(或子問題)因條件而被隱藏,整個運算式就會失效。
例如:只要 Q1 的其中一個子問題被篩選掉,count(Q1_SQ1,Q1_SQ2,Q1_SQ3,Q1_SQ4) 就一律傳回空字串。 若要計算此類問題中已勾選的子問題數量,可使用 count(Q1_SQ1.NAOK,Q1_SQ2.NAOK,Q1_SQ3.NAOK,Q1_SQ4.NAOK)。如果子問題被隱藏,Expression Manager 會傳回空字串。
若不使用 NAOK:只要有一個問題或一個子問題被隱藏,Expression Manager 就一律傳回空字串,等同於傳回 false。
.shown 一律採用 NAOK 機制(隱藏時為空字串),但如果您需要的是答案的代碼,建議一律在問題編號後加上 .NAOK,除非您確實需要不加,且清楚自己在做什麼。
另一個範例與相關資訊請參閱 覆寫串接條件
函數的使用
Expression Manager 提供數學、字串及使用者自訂函數,如下所示。這些函數均有對應的 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 | 使用數值的輸入表與輸出表來轉換數值 | 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 (Updated in 3.0.2 ) | 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) |
| log | 數字的對數,以 base 為底;若未指定 base,則為自然對數。 | number log(number,base=e) |
| 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) |
規劃中或考慮中的函數
其他已規劃(或考慮中)但尚未實作的函數包括下列項目。其中部分函數是為了與其他問卷工具保持向下相容。
| 語法 | 意義 | 備註 |
|---|---|---|
| e() | 傳回 e 的值 | |
| formatDate(X,PAT) | 傳回依照 Java 日期格式樣式 PAT 格式化後的日期 X 的字串值 | |
| formatNumber(X,PAT) | 傳回依照 Java 數字格式樣式 PAT 格式化後的數字 X 的字串值 | |
| getAnsOption(X) | 核心外掛程式 ExpressionAnswerOptions | |
| getAnsOption(X,Y) | 核心外掛程式 ExpressionAnswerOptions | |
| getRelevance(X) | 傳回問題 X 的相關性等式 | |
| getStartTime() | 傳回訪談開始時對應的系統時間日期 | |
| getType(X) | 傳回資料類型的字串名稱,例如 isNA() 為真時傳回 *NA* | |
| gotoFirst() | 跳至第一組相關的問題 - 這會違反系統的正常流程 | |
| gotoNext() | 跳至下一組相關的問題 - 這會違反系統的正常流程 | |
| gotoPrevious() | 跳至上一組相關的問題 - 這會違反系統的正常流程 | |
| isAsked(X) | 若答案不是 *NA*、*INVALID* 也不是 *UNASKED*,則傳回 true | |
| isInvalid(X) | 若答案類型為 *INVALID*,則傳回 true | |
| isNA(X) | 若答案類型為 *NA*,則傳回 true | |
| isNotUnderstood(X) | 若答案類型為 *HUH*,則傳回 true | |
| isRefused(X) | 若答案類型為 *REFUSED*,則傳回 true | |
| isSpecial(X) | 若答案類型為 *UNASKED*、*NA*、*REFUSED*、*INVALID*、*UNKNOWN* 或 *HUH*,則傳回 true | |
| isUnknown(X) | 若答案類型為 *UNKNOWN*,則傳回 true | |
| jumpTo(X) | 跳至包含指定名稱問題的群組 -- 這會違反系統的正常流程 | |
| jumpToFirstUnasked() | 跳至第一個尚未詢問的問題 |
|
| lastIndexOf(X,Y) | 傳回字串 Y 在字串 X 中最後出現的索引(從 0 起算)。若 X 中不包含 Y,則傳回 -1 | |
| list(X,...) | 以逗號分隔的正值清單字串,最後兩項之間以「和」連接 | |
| mean(X,...) | 傳回一組值的平均數 | |
| numAnsOptions(X) | 傳回問題 X 的答案選項數量 | |
| orlist(X,...) | 以逗號分隔的正值清單字串,最後兩項之間以「或」連接 | |
| parseDate(X,PAT) | 傳回依照 Java 日期格式樣式 PAT 解析字串 X 後得到的日期值 | |
| parseNumber(X,PAT) | 傳回依照 Java 數字格式樣式 PAT 解析字串 X 後得到的數值 | |
| showAllResponsesExcept( questionList,attributeList,attributeTitleList) | questionList = 以豎線分隔的問題識別碼清單;attributeList = 以豎線分隔的屬性清單(例如 question#、title、text、type,可由您決定要顯示的內容);attributeTitleList = 以豎線分隔的表格標題清單,以便將報表國際化。 | |
| showTheseResponses( questionList,attributeList,attributeTitleList) | questionList = 以豎線分隔的問題識別碼清單;attributeList = 以豎線分隔的屬性清單(例如 question#、title、text、type,可由您決定要顯示的內容);attributeTitleList = 以豎線分隔的表格標題清單,以便將報表國際化。 |
Expression Manager 知道哪些變數是本機變數
為了正確建立頁面的 JavaScript,Expression Manager 必須知道哪些變數是在該頁面上設定的,以及它們的 JavaScript ID(例如用於 document.getElementById(x))。它還必須知道哪些變數是在其他頁面上設定的,以確保所需的 <input type='hidden' value='x'> 欄位存在並已填入資料。
串聯條件
只要有任何一個變數不相關,整個等式就會不相關(false)。例如,在下表中,N/A 表示其中一個變數不相關
| 運算子 | 範例 | a | b | 結果 |
|---|---|---|---|---|
| + (unary) | +a | N/A | false | |
| ! | !a | N/A | false | |
| == (或 eq) | a == b | N/A | 5 | false |
| == (或 eq) | a == b | N/A | 0 | false |
| == (或 eq) | a == b | N/A | N/A | false |
| != (或 ne) | a != b | N/A | 5 | false |
| != (或 ne) | a != b | N/A | N/A | false |
| != (或 ne) | a != b | N/A | 0 | false |
| > (或 gt) | a > b | N/A | 5 | false |
| >= (或 ge) | a >= b | N/A | 5 | false |
| < (或 lt) | a < b | N/A | 5 | false |
| <= (或 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 |
| 函式 | sum(a,b,b) | N/A | 5 | false |
| 函式 | max(a,b) | N/A | 5 | false |
| 函式 | min(a,b) | N/A | 5 | false |
| 函式 | implode(', ',a,b,a,b) | N/A | 5 | false |
| 函式 | if(a,a,b) | N/A | 5 | false |
| 函式 | is_empty(a) | N/A | false | |
| 函式 | is_empty(a) | 0(或空白) | true | |
| 函式 | !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 是可接受的)。在這種情況下會發生下列行為。假設您有一個變數 q1.NAOK
- q1 不會被加入 LEManyNA() 子句
- LEMval('q1') 仍會檢查該作答結果是否相關,若不相關則傳回 ''(因此個別不相關的作答結果會被忽略,但不會使整個運算式失效)。
因此,累計總和問題的解決方法是使用等式 sum(q1.NAOK,q2.NAOK,q3.NAOK,...,qN.NAOK)。
使用 .NAOK 後綴還能讓作者設計出具有多條可能路徑、但稍後又匯合到共同路徑的問卷。例如,假設受訪者的作答超出正常範圍。作者可以提醒受訪者可能無法得到有效的結果,並詢問他們是否真的要繼續問卷。如果他們回答「是」,就會顯示其餘的問題。「其餘問題」的條件會檢查最初的作答是否在正常範圍內,或者受訪者是否對那個僅在超出正常範圍時才相關的問題回答了「是」。
Expression Manager 如何支援條件式微調客製化?
以下是微調客製化的範例(其中題型 =='expr' 表示等式):
| 問題編號 | 相關性 | 題型 | 問題 |
|---|---|---|---|
| name | 1 | text | 您的姓名是? |
| age | 1 | text | 您幾歲? |
| badage | !is_empty(age) | expr | {(age<16) or (age>80)} |
| agestop | badage | message | 抱歉,{name},就此測試而言,您的年齡{if( (age<16),'young',if( (age>80),'old','middle-aged') ) }了。 |
| kids | !badage | yesno | 您有小孩嗎? |
| parents | 1 | expr | {!badage && kids=='Y'} |
| numKids | parents | text | 您有幾個小孩? |
| kid1 | parents && numKids >= 1 | text | 您的第一個小孩幾歲? |
| kid2 | parents && numKids >= 2 | text | 您的第二個小孩幾歲? |
| kid3 | parents && numKids >= 3 | text | 您的第三個小孩幾歲? |
| kid4 | parents && numKids >= 4 | text | 您的第四個小孩幾歲? |
| kid5 | parents && numKids >= 5 | text | 您的第五個小孩幾歲? |
| sumage | 1 | expr | {sum(kid1.NAOK,kid2.NAOK,kid3.NAOK,kid4.NAOK,kid5.NAOK)} |
| report | parents | yesno | {name},您表示您 {age} 歲,並且有 {numKids} 個小孩。您前 {min(numKids,5)} 個小孩的年齡總和為 {sumage} |
所有這些問題都可以放在同一頁(例如在同一個群組中),而且只會顯示相關的問題。此外,當您輸入小孩的年齡時,最後一個問題中的 sum() 運算式會在頁面上動態更新。
Expression Manager 透過以具名的 <span> 元素包住每個運算式來提供此功能。每當值改變時,它就會重新計算應顯示在該 <span> 元素中的運算式,並重新產生顯示內容。同一頁面上可以有數十甚至數百個這類客製化運算式,頁面只會以一次畫面更新重新顯示所有運算式。
LimeSurvey 1.91+ 與 Expression Manager 功能對照
| 舊功能 | 新功能 | 備註 |
|---|---|---|
| 條件 | 相關性 | 您可以使用非常複雜的條件等式,並存取更廣泛的變數 |
| 評估 | 等式 | 任何評估分數都可以重新組織為等式。這既能確保分數寫入資料庫,也能讓您在目前頁面上看到分數值的動態變化 |
| 取代 | Expression Manager | 核心引擎會接收輸入字串,並將大括號內的所有內容視為運算式 - 因此它能處理所有歷來的取代類型。為避免干擾內嵌的 JavaScript,Expression Manager 僅在 (a) 大括號內沒有前導或尾隨空白時才會處理大括號之間的內容 - 例如 {expr} 是運算式,但 { expr}、{expr } 和 { expr } 不是運算式。此外,Expression Manager 不會處理其自身字串內的內容(例如 {list('hi','there {braces}')} 會產生 "hi there {braces}")。它也會忽略已跳脫的大括號(例如 \{this is not an expression\}) |
| 驗證 | Expression Manager | 計畫是取用目前的最小值/最大值問題屬性,並交由 Expression Manager 處理。如此一來,最小值/最大值本身也可以是運算式 |
語法醒目提示
為了協助輸入和驗證運算式,EM 提供具有下列功能的語法醒目提示:
語法醒目提示的類型與意義
| 顏色 | 範例 | 意義 | 提示文字 | 備註 |
|---|---|---|---|---|
| 淺褐色背景 | 範例 | 整個方程式 | 無 | 大括號內任何被識別為方程式的內容(例如前後沒有空白字元)都會以淺褐色背景標示,以便與周圍的文字區分 |
| 粗體紅色文字 | 範例 | 錯誤 | 錯誤的相關說明 | 可能是未知的變數,或是函數中的錯誤等。問卷可能會完全無法運作,但此訊息不會顯示給一般使用者。 |
| 藍色文字 | 範例 | 函數名稱 | 含義及允許的語法 | 函數名稱,或後面接著左括號而應為函數的項目,會以粗體藍色文字顯示。提示文字會顯示該函數的含義及允許的語法。 |
| 灰色文字 | 範例 | 字串 | 無 | 單引號和雙引號括住的字串會以灰色文字顯示 |
| 青色文字 | 範例 | 在同一頁設定的變數, | [名稱或 SGQA 代碼]:問題;值;顯示各個值所對應代碼的答案清單 | 凡是在目前問題所在的同一頁設定的變數,都會以青色文字顯示,表示可在 JavaScript 中更新。提示文字會顯示其名稱、實際問題及現在值(若未設定則為空白)。若問題類型預期的回答來自列舉值集合,則會顯示代碼與顯示值之間的對應。 |
| 綠色文字 | 範例 | 在前面頁面設定的變數 | [名稱或 SGQA 代碼]:問題;值;顯示各個值所對應代碼的答案清單 | 凡是在前面頁面設定的變數,都會以粗體綠色文字顯示。提示文字會顯示其名稱、實際問題及現在值(若未設定則為空白)。若問題類型預期的回答來自列舉值集合,則會顯示代碼與顯示值之間的對應。 |
| 粗體粉紅色文字 | 範例 | 在後面頁面設定的變數 一般而言:問卷開始時為空白,但可透過索引或返回上一頁填入 | [名稱或 SGQA 代碼]:問題;值;顯示各個值所對應代碼的答案清單 | 凡是在後面頁面設定的變數,都會以粗體粉紅色文字顯示。提示文字會顯示其名稱、實際問題及現在值(若未設定則為空白)。若問題類型預期的回答來自列舉值集合,則會顯示代碼與顯示值之間的對應。 |
| 粗體褐色文字 | 範例 | LimeSurvey 替換值 | 值 | LimeSurvey 替換字串(如 {TOKEN:xxx}、{PRIVACY_MESSAGE})會以粗體褐色文字顯示。 |
| 紅色文字 | 範例 | 指派運算子 (=) | 警告訊息 | 若您使用指派運算子 (=),該運算子會以紅色文字顯示。這是為了避免在您其實想檢查 a == b 時,意外地設定了 a = b 而重新指派數值。 |
| 一般黑色文字 | 範例 | 標點符號 | 無 | 運算式中所有其他標點符號都會以一般黑色文字顯示。 |
| 紅框文字 | 錯誤周圍會有一條粗紅線 | 語法錯誤 | 錯誤的說明 | 偵測到的任何語法錯誤都會以紅框圍住錯誤處。提示文字會顯示錯誤內容。例如括號不成對、使用未定義的函數、傳遞給函數的引數數量錯誤、運算式結構不良(如變數之間缺少運算子)、嘗試對唯讀變數指派新值、嘗試對非變數指派值,或使用未支援語法。請注意,即使運算式中有多個錯誤,語法錯誤偵測系統可能只會回報一個錯誤;不過只要偵測到錯誤,至少會顯示一個錯誤。 |
延伸閱讀
Expression Manager 範例問卷
使用案例與操作指南
逐步範例
Expression Manager 統計函數
開發人員參考資料










