Practical Examples
The classic first program in SLIPS:
import SLIPS
let env = CLIPS.createEnvironment()
CLIPS.eval(expr: """
(defrule hello
=>
(printout t "Hello, SLIPS!" crlf))
""")
CLIPS.run(limit: nil)
CLIPS.eval(expr: """
(deftemplate person
(slot name (type STRING))
(slot age (type INTEGER))
(slot city (type STRING)))
""")
// Assert
let id1 = CLIPS.eval(expr: "(assert (person (name \"John\") (age 30) (city \"NYC\")))")
let id2 = CLIPS.eval(expr: "(assert (person (name \"Jane\") (age 25) (city \"LA\")))")
// List facts
CLIPS.eval(expr: "(facts)")
// Retract
CLIPS.eval(expr: "(retract \(id1))")
CLIPS.eval(expr: """
(defrule adult
(person (name ?n) (age ?a&:(>= ?a 18)))
=>
(printout t ?n " is an adult" crlf))
""")
CLIPS.eval(expr: """
(defrule same-city
(person (name ?n1) (city ?c))
(person (name ?n2&~?n1) (city ?c))
=>
(printout t ?n1 " and " ?n2 " live in " ?c crlf))
""")
CLIPS.eval(expr: """
(defrule no-orders
(customer (id ?id) (name ?n))
(not (order (customer-id ?id)))
=>
(printout t "Customer " ?n " has no orders" crlf))
""")
Simplified medical diagnosis system:
CLIPS.eval(expr: """
(deftemplate symptom
(slot name)
(slot severity (type INTEGER)))
(deftemplate diagnosis
(slot disease)
(slot probability (type FLOAT)))
(defrule flu
(symptom (name fever) (severity ?s1&:(> ?s1 7)))
(symptom (name cough))
(symptom (name headache))
=>
(assert (diagnosis (disease flu) (probability 0.85)))
(printout t "Probable flu (85%)" crlf))
(defrule cold
(symptom (name cough))
(symptom (name stuffy-nose))
(not (symptom (name fever) (severity ?s&:(> ?s 7))))
=>
(assert (diagnosis (disease cold) (probability 0.75)))
(printout t "Probable cold (75%)" crlf))
""")
// Enter symptoms
CLIPS.eval(expr: "(assert (symptom (name fever) (severity 8)))")
CLIPS.eval(expr: "(assert (symptom (name cough) (severity 6)))")
CLIPS.eval(expr: "(assert (symptom (name headache) (severity 5)))")
// Execute
CLIPS.run(limit: nil)
CLIPS.eval(expr: """
(defrule urgent
(salience 100)
(alarm ?type)
=>
(printout t "URGENT: " ?type crlf))
(defrule normal
(salience 0)
(message ?msg)
=>
(printout t "Message: " ?msg crlf))
""")
// Depth (default)
CLIPS.eval(expr: "(set-strategy depth)")
// Breadth
CLIPS.eval(expr: "(set-strategy breadth)")
// LEX (Recency)
CLIPS.eval(expr: "(set-strategy lex)")
CLIPS.eval(expr: """
(defrule calculate-discount
(order (id ?id) (amount ?amt))
(customer (id ?cid) (type premium))
=>
(bind ?discount (* ?amt 0.20))
(bind ?final (- ?amt ?discount))
(printout t "Order " ?id ": $" ?final " (discount $" ?discount ")" crlf))
""")
CLIPS.eval(expr: """
(deftemplate order
(multislot products))
(defrule analyze-order
(order (products $?items))
=>
(printout t "Product count: " (length$ $?items) crlf))
""")
// Enable watch facts
CLIPS.eval(expr: "(watch facts)")
// Enable watch rules
CLIPS.eval(expr: "(watch rules)")
// Output:
// ==> (person (name "John") (age 30))
// ==> Activation adult
// FIRE adult
// Disable
CLIPS.eval(expr: "(unwatch facts)")
CLIPS.eval(expr: "(unwatch rules)")
// Define template
CLIPS.eval(expr: """
(deftemplate person
(slot name)
(slot age)
(slot city))
""")
// Create fact
CLIPS.eval(expr: "(assert (person (name \"John\") (age 30) (city \"Rome\")))")
// ==> f-1
// Modify existing fact
CLIPS.eval(expr: "(modify 1 (age 31) (city \"Milan\"))")
// ==> f-2 (new fact with modifications)
// Duplicate with modifications
CLIPS.eval(expr: "(duplicate 1 (name \"Jane\"))")
// ==> f-3 (Jane, 31, Milan)
// List all slots of a template
CLIPS.eval(expr: "(deftemplate-slot-names person)")
// Output: (create$ name age city)
// Check if a slot is multifield
CLIPS.eval(expr: "(deftemplate-slot-multip person name)")
// Output: FALSE
// Check slot existence
CLIPS.eval(expr: "(deftemplate-slot-existp person salary)")
// Output: FALSE
// Define modules
CLIPS.eval(expr: """
(defmodule BILLING
(export ?ALL))
(defmodule SHIPPING
(export ?ALL))
(defmodule MAIN
(import BILLING ?ALL)
(import SHIPPING ?ALL))
""")
// Define rules in different modules
CLIPS.eval(expr: """
(defmodule BILLING)
(defrule calculate-total
(declare (salience 10))
(order (amount ?a))
=>
(printout t "Total: $" ?a crlf))
(defmodule SHIPPING)
(defrule prepare-shipment
(order (id ?id))
=>
(printout t "Preparing shipment for order " ?id crlf))
""")
// Set focus to control execution order
CLIPS.eval(expr: "(focus BILLING SHIPPING)")
// Run will execute BILLING first, then SHIPPING
CLIPS.run(limit: nil)
CLIPS.eval(expr: """
(defrule suspicious-price
(product (name ?n) (price ?p))
(test (or (< ?p 0) (> ?p 10000)))
=>
(printout t "ALERT: " ?n " has anomalous price: $" ?p crlf))
""")
CLIPS.eval(expr: """
(defrule any-age
(person (name ?n) (age ?))
=>
(printout t "Found person: " ?n crlf))
""")
import SLIPS
// Register custom function
var env = CLIPS.createEnvironment()
env.functionTable["my-func"] = FunctionDefinitionSwift(name: "my-func") { env, args in
guard let val = args.first, case .int(let i) = val else {
return .none
}
return .int(i * 2)
}
CLIPS.eval(expr: """
(defrule test
=>
(printout t "Result: " (my-func 21) crlf))
""")
CLIPS.run(limit: nil) // Output: Result: 42
var logs: [String] = []
Router.AddCallback(&env, name: "custom", priority: 10) { _, _, text in
logs.append(text)
return true
}
CLIPS.eval(expr: "(printout custom \"Log message\" crlf)")
print(logs) // ["Log message\n"]