Swift小筆記-Functions and Closures

函數和閉包

使用func來聲明一個函數,使用名字和參數來調用函數。使用->來指定函數返回值。

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")

練習:
刪除day參數,添加一個參數來表示今天吃了什麼午飯。

使用元組來讓一個函數返回多個值。該元組的元素可以用名稱或數字來表示。

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }

    return (min, max, sum)
}
let statistics = calculateStatistics([5, 3, 100, 3, 9])
statistics.sum
statistics.2

函數可以帶有可變個數的參數,這些參數在函數內表現為數組的形式:

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(42, 597, 12)

練習:
寫一個計算參數平均值的函數。

函數可以嵌套。被嵌套的函數可以訪問外側函數的變量,你可以使用嵌套函數來重構一個太長或者太復雜的函數。

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
returnFifteen()

函數是第一等類型,這意味著函數可以作為另一個函數的返回值。

func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

函數也可以當做參數傳入另一個函數。

func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

函數實際上是一種特殊的閉包,你可以使用{}來創建一個匿名閉包。使用in將參數和返回值類型聲明與閉包函數體進行分離。

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

練習:
重寫閉包,對所有奇數返回0。

有很多種創建閉包的方法。如果一個閉包的類型已知,比如作為一個回調函數,你可以忽略參數的類型和返回值。單個語句閉包會把它語句的值當做結果返回。

let mappedNumbers = numbers.map({ number in 3 * number })
mappedNumbers

你可以通過參數位置而不是參數名字來引用參數——這個方法在非常短的閉包中非常有用。當一個閉包作為最後一個參數傳給一個函數的時候,它可以直接跟在括號後面。

let sortedNumbers = sorted(numbers) { $0 > $1 }
sortedNumbers

Swift小筆記-Control Flow

控制流

使用ifswitch來進行條件操作,使用for-inforwhiledo-while來進行循環。包裹條件和循環變數括號可以省略,但是語句的大括號是必須的。

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
teamScore

 

if語句中,條件必須是一個布林表達式——這意味著像if score { ... }這樣的代碼將報錯,而不會隱式地與 0 做對比。

你可以一起使用iflet來處理值缺失的情況。有些變數的值是可選的。一個可選的值可能是一個具體的值或者是nil,表示值缺失。在類型後面加一個問號來標記這個變數的值是可選的。

var optionalString: String? = "Hello"
optionalString == nil

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}

練習:
optionalName改成nil,greeting會是什麼?添加一個else語句,當optionalNamenil時給greeting賦一個不同的值。

如果變數的可選值是nil

條件會判斷為false,大括號中的代碼會被跳過。如果不是nil,會將值賦給let後面的常量,這樣代碼塊中就可以使用這個值了。

 

switch支援任意類型的數據以及各種比較操作--不僅僅是整數以及測試是否相等。

let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(x)?"
default:
    let vegetableComment = "Everything tastes good in soup."
}

練習:
刪除default語句,看看會有什麼錯誤?

運行switch中匹配到的子句之後,程序會退出switch語句,並不會繼續向下運行,所以不需要在每個子句結尾寫break

 

你可以使用for-in來遍歷字典,需要兩個變數來表示每個鍵值對。字典是一個無序的集合,所以他們的鍵和值以任意順序迭代結束。

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
largest

練習:
添加另一個變數來記錄哪種類型的數字是最大的。

使用while來重覆運行一段代碼直到不滿足條件。循環條件可以在開頭也可以在結尾。

var n = 2
while n < 100 {
    n = n * 2
}
n

var m = 2
do {
    m = m * 2
} while m < 100
m

你可以在循環中使用..<來表示範圍,也可以使用傳統的寫法,兩者是相等的:

var firstForLoop = 0
for i in 0..<4 {
    firstForLoop += i
}
firstForLoop

var secondForLoop = 0
for var i = 0; i < 4; ++i {
    secondForLoop += i
}
secondForLoop

使用..<創建的範圍不包含上限,如果想包含的話需要使用...(for i in 0...5)

Swift小筆記-Simple Values

注意:Swift的述敍句不用分號(;)結尾
宣告:
var宣告變數
var myVariable = 32
let宣告常數
let myConstant = 32
宣告變數或常數時若無指定型別,Swift會自動判別
若要指定型別,在變數/常數名稱後面加上 :型別 即可
var myVariableInt: Int = 32
let myConstantFloat: Float = 32(會被判別成32.0)
轉型
常見的轉型像是數字轉字串,只要在數值變數前加上(String)即可
var label = “The width is “
var width = 94
var  widthLabel =label + (String)width
有個更快的方法,就是將數值變數用反斜線加一組小括號包起來
let apples = 3
let oranges = 5
let appleSummary = “I have \(apples) apples."
let fruitSummary = “I have \(apples + oranges) pieces of fruit."
陣列&字典
Array與Dictionary的宣告使用一組中括號 [ ]
Array範例:
var shoppingList = [“PS4″, “XboxOne", “WiiU"]
用index來存取:
shoppingList[2](得到WiiU)
shoppingList[1] = “UC RX-0″(XboxOne會被改成UC RX-0)
Dictionary範例:
var myFamily = [“Angel": 37, “Shawn": 6]
用鍵(key)來存取
myFamily[“Angel"](得到37)
myFamily[“Anna"] = 36(增加一個元素,鍵為Anna值為36,進到myFamily裡,順序不定)
如果要建立一個空陣列或空字典,可以用初始化語法
var emptyArray = [String]()
var emptyDictionary = [String: Float]()
myArray = [ ]
myDictionary = [:]

漫談系統分析師的工作技能

1.專業知識

分析師需要的專業知識可概分為三類:軟體工程、專案管理與業務領域。首先,由於需求分析的重要性,軟體工程學中針對需求分析已發展出很多重要技術與方法,例如:使用案例(use case)與資料建模(data modeling),分析師應該要學習這些知識並靈活運用於專案中。此外,分析師也必須嫻熟所謂需求工程與管理的技能,了解需求除了建立外,也需要管理,不能放任需求無限制擴散,而且必須將需求建立和管理活動平衡地貫穿整個產品的生命週期。過程中分析師也必須具備健全的專案管理、風險管理和品質要求的專業知識,以及如何在軟體開發生命週期的不同階段運用這些知識來確保專案的成功。

最後,業務領域的基本知識更是分析師在工作上不可少的資產。有了與業務相關的基本知識,分析師才得以與客戶進行有效的溝通、減少因溝通不良而造成的對系統需求的偏差;並進而得以從交談或字裡行間發掘出客戶未說明的假設與隱含的要求。甚至也可以提出客戶沒想到的寶貴功能或可以改善客戶業務流程效率的具體建議。最後,如果沒有適當的業務領域知識,分析師往往就無法分辨出客戶提出的一些過度的或沒必要的功能。

這裡筆者也要提醒讀者:領域知識固然是建構資訊系統時不可或缺的基礎,但僅僅有深厚的領域知識也不足以完成一個複雜的資訊系統,過度的強調領域知識其實只是一個迷思。身為一個系統分析師,我們首要任務是(創造性的)轉化使用者的業務需求成系統的功能與非功能需求,而不是學習可能無窮盡的領域知識。我們是系統分析師而不是所謂的領域專家,我們的專業是去理解使用者的需求,進行歸納分析與轉化的工作,如何把這件事做好才是我們自己的領域知識。

 

身為一個系統分析師,我們首要任務是(創造性的)轉化使用者的業務需求成系統的功能與非功能需求,而不是學習可能無窮盡的領域知識。

 

業務領域的基本知識是協助我們了解客戶需求的工具與手段,而非目的,除非我們工作生涯中僅僅需要浸淫於一個單純的領域,否則我們勢必要接觸不同的領域,那怎麼有可能每個領域我們都是專家。客戶才是我們關於領域知識的最佳教師,我們要的是如何從客戶身上快速學習與吸收領域知識的能力。這方面,軟性技能就顯得額外重要。

2.軟性技能

一個好的分析師除了要具備扎實的專業知識外,也必須有較強的溝通力、促成力(facilitation),與人際關係能力。此外,性格上願意耐心和真誠地與人互動合作也是分析師工作成功的關鍵因素之一,以下我們就來談談一些可以幫助分析師做好工作的軟性技能。

3.聆聽

無庸置疑,聆聽的技能是有效溝通的第一步。聆聽的過程首重專注,不僅要禀除雜念,保持積極的姿勢和眼神接觸,並要適時重申關鍵點,以確認您的理解是正確的。你需要掌握訪談對象說的話,也要從字裡行間發現他們猶豫不決而未說出的重點。此外,要盡可能的以訪談對象偏好的溝通方式進行交談,客觀的記錄他們的話語,極力避免在其中參入你個人的解讀。同時注意 訪談對象話語中的任何潛在的假設以及自己在解讀時所依賴的基礎假設。

4.面談和提問

客戶需求的建立與確認少不了要面談與討論, 這個過程中,提問是最主要的手段,所以一個分析師必須能夠提出正確的問 題。這裡所謂的正確問題,不僅是能有效但被動地蒐集客戶的需求而已,還必須是主動且具有引導性的問題。例如,客戶在描述需求時通常只集中在系統的正常與預期的行為。然而,造成日後系統開發上的複雜或困難的地方,多半是異常狀況的處理。所以分析師就要主動提出有關非預期狀況的問題,像是有哪些例外可能會發生,一旦發生後,使用者如何因應之類的問題,這樣才可以決定該如何處理該系統應預期的異常壯況。時時揣摩,隨著經驗的積累,分析師就能熟練提問的技巧,關鍵性地提出一些正確的問題,有效的釐清客戶需求的不確定性或分歧的地方,挖掘出客戶未明言的假設和期望,進而達到所謂引導客戶需求(requirement elicitation)的境界。

5.分析力

顧名思義,系統分析師一定要具備相當的分析能力,能夠從繁瑣的業務程序與紊亂的事務中找出脈絡,這牽涉到的複雜的心智活動,一方面要能拆解(break down)並看出埋藏在眾多事務之間更基本的元素,另一方面還要能找出這些基本元素的結構相依性與因果關係。打個比方,好像是中學數學裡所學的因式分解的課題,既要分解也要重組。這中間的基礎就是抽象化的技能:您要能夠自在地遊走於各個抽象層級,有時候,你必須能從高層次的資訊演繹出繁瑣的細節。在其他情況下,你需要從個別客戶的特定需求推廣發展出一套可適用於一群客戶的需求。進而可以用嚴格的角度檢視從不同來源所收集到的資訊,加以整理調和,區分出哪些是客戶真正需要的,而哪些只是他們說他們需要的。

6.促成力

舉辦需求工作坊(workshop),邀請與系統有關的客戶代表討論系統需求是一個建立客戶需求常用的一個手段,但要讓這類工作坊有成效就需要一個中立的促成者(facilitator),透過強有力的提問和觀察技巧,一方面營造一個讓各方參與者可以信任的氣氛,暢所欲言,並降低業務與資訊科技人員之間不時有的緊張關係;一方面也要引導參與者聚焦在議題上, 避免進入發散、漫無目的的爭論中,在有限時間內獲致具體的結論,這也是分析師要扮演的角色。有興趣的讀者可以參考Ellen Gottesdiener所撰寫的 「Requirements by Collaboration: Workshops for Defining Needs 」(Addison – Wesley出版 社,2002)一書中對促成者角色扮演的具體建議。

7.觀察力

敏銳的觀察力也是分析師應該要積極 培養的技能之一。舉例而言,認真觀看客戶如何執行他的日常工作或是如何使用既有的應用系統後,分析師應該要能夠發現一些客戶可能沒有提到微妙之處,從而發掘出應該要提出來討論的新需求。

8.書寫

分析師的主要工作產出就是系統需求 的書面規範,客戶、專案(產品)經理和開發人員都依賴這份文件進行溝通。不用說,如果分析師的書寫能力差,交出來的文件語意不清而難以閱讀,對整個系統的開發勢必造成極為嚴重的負面影響。不過這裡所著重的不是文字的優美,而是如何清楚地描述出系統的各個面向,用語應力求精準,盡量避免模棱兩可的詞句。除了文字之外,也要搭配繪圖,畢竟圖形能夠傳遞比文字更高密度的資訊。

9.組織力

需求蒐集與整理的過程中免不了要面 對許多龐雜無序的資訊,分析師必須以耐心和恆心對這些資訊進行歸納整理,一方面以 分析力找出眾多資訊間的基本元素與關係,另一方 面也要對這些元素與衍生的事物加以組織,快速建構出一個邏輯清晰的整體性架構,來轉化使用者需求成系統需求。如果組織能力弱,一旦面對龐大複雜的資訊時,很容易就會挂一漏萬,或是不時發生不一致的矛盾,必須時時進行修正。

10.人際關係

分析師的工作對象就是人,而且是有著不同背景與不同利益關係的各路人馬。要順利完成系統分析的工作,分析師必須能讓這些人跟自己合作,朝共同的目標邁進。過程中,免不了要跟各種工作職能和各級組織的人員交涉,這時候一些必要的人際交際手腕是一定需要的。否則,人的問題將遠遠比技術性問題難解決。

11.創新力

分析師雖然不見得要能創造需求,但也絕對不能只當個需求訪談員,單純地抄錄使用者的需求談話而已。我們一再強調分 析師的工作是將使用者的需求「轉化」成系統的需求,這裡的轉化是一種創造性的轉化,而不是機械性的將使用者的需求逐條地「翻譯」成系統需求。 這個過程中分析師一定得發揮個人的創造力,去解讀、去詮釋使用者的需求,進而提出可符合使用者 需求的系統需求。

Angular.js 學習小筆記(二)

AngularJS怎麼與HTML互動:
 
上圖的例子就是一種,ng-controller宣告的名稱就是function的名稱
建立Module:建立一個名為app.js的檔案,在裡面宣告變數 app
變數 app是用angular.module()方法,第一個參數是 Application名稱,第二個參數是其它需要引用的函數
引用app.js:
下圖是html與app.js之間的關係:
表示式:
AngularJS的表示式是寫在兩組大括號裡{{ }}
例如:
更多的表示式可以查詢:http://docs.angularjs.org/guide/expression
引用Module:
在html(或要引用的區塊)加入 ng-app,並指派app的名稱
<html ng-app="store"> or <body ng-app="store"> 都可以
在這個區塊裡AngularJS表示式都能運作
Controller:
上圖有提到,要習慣將 app.js 做閉包(Closure)
關於Javascript閉包的資料:網路上這份資料可以參考
回到範例,在 app.js 我們有建立一個 app(var app = angular.module(‘store’, [ ]);)
在建立完 app 之後,就可以開始寫Controller了
另外再建立一個物件變數 gem,給它 name / price / description等等屬性
然後再回到Controller裡使用這個物件
這樣就把Controller與物件連結起來了,接著要在頁面上使用必須先在
 
如上圖所示,先建立一個 div,把基本的排版準備好(假資料),待會兒再替換掉
來看一下如何介接Controller(及裡面連結的資料)
在上圖有先建立一個 div,也把要顯示的假資料放好了,所以要在這個 div 指定給AngularJS
所以在 div 上加上一個 ng-controller,表示 Controller的資料及處理將在這個 div 內
<div ng-controller="StoreController as store">
ng-controller=指示器
StoreController=Controller名稱
store=在view裡使用的別名
這段的意思是將我們在 app.js 裡的 StoreController 別名(在html裡使用的名稱)為 store
上面都做好之後,我們就可以把假資料替換成 app.js 裡宣告的資料了
前面有提到,AngularJS在 view 裡的表示式要用兩組大括號包起來
所以,{{store.product.name}} 將會連結到 app.js 裡宣告的 StoreController裡宣告的 this.product
然後再連結到 var gem(物件)及其屬性,store.product.price 及 store.product.description 也一樣
如果,把AngularJS表示式搬到處理區(<div ng-controller="StoreController as store">)之外呢?
則該AngularJS表示式不會被印出來,因為它不會被認得是什麼東西
判斷 show or hide:
來加個按鈕
 
我們在畫面上加一個按鈕,顯示名稱為 Add to Cart(加入購物車)
然後在資料來源 gem 裡加上一個屬性「canPurchase」(是否可購買),並給它一個 false 值
接下來要如何讓這個按鈕會依 canPurchase 的值來決定要不要顯示呢?
上圖可以看到,我們在按鈕的屬性裡加上一個 ng-show
ng-show 後面的判斷式為真(true)時,這個按鈕才會顯示出來
所以,如果 gem 的 canPurchase 的值為 true 時,這個按鈕才會出現
在 gem 裡加上一個屬性「soldOut」(是否售完),並給它一個 true 值
像上圖,用 ng-show 來做反向判斷(ng-show="!store.product.soldOut")太不好閱讀了,容易讓維護出錯
所以我們用 ng-hide 來讓符合條件的區塊隱藏起來
像上圖,閱讀起來就清楚明瞭多了,當 soldOut 為 true 時(ng-hide="store.product.soldOut")
就把包覆商品資訊的 <div> hide起來~
陣列物件用跑迴圈來取出裡面每一個元素:
上面都是從 app.js 丟一個普通物件給前端,那遇到後端丟出來的是陣列物件的時候,該怎麼取得內部的元素呢?
可以使用 ng-repeat 來取得
我們先把 app.js 裡的 gem 物件改成陣列物件
資料來源改成陣列之後,再來看看前端要做什麼修改
這是原本的架構改成存取陣列物件的樣子(加上index [0]是取陣列的第一個元素)
但這樣寫只能取陣列的第一筆,如果有三筆就要寫三塊(store.products[1]和store.products[2])
而且還不曉得陣列裡的元素到底有幾筆,所以這樣寫很不適當(如下圖)
這個時候就要用 ng-repeat 來自動處理了
因為之前只有用 StoreController as store 來轉換別名,並使用別名.物件名.屬性名(store.product.name)來存取資料
現在要多加一層 ng-repeat="product in store.products",代表要用 ng-repeat 來遍歷 store.products 這個陣列物件
如此一來,不管 store.products 將包含幾個元素都不用我們來苦惱了
目前為止,我們學會了
Directives-HTML如何觸發Javascript的行為
Modules-我們的應用程式元件在哪在動作、如何動作
Controllers-我們要在哪裡增加應用程式的行為
Expressions-如何在頁面上顯示變數的值

Angular.js 學習小筆記(一)

一、基本hello world
建立一個最基本 html檔案,並在 html tag內加上“ng-app”,宣告這是個 angular的app
這是AngularJS用來啟動的關鍵字,DOM載入後,AngularJS就會開始尋找這個字,找到的話,就會把這頁面當成是AngularJS應用程式
如果只是局部需要用到,而非整頁,也可以把這個字放在要應用到AngularJS的div中
直接引用線上的 angular.min.js(筆記當下是1.3.1最新),就不用另外下載了
在 input tag裡加上 ng-model,並命名為“yourName”,先把它當成是資料來源
即宣告由這個 input tag來接收變數“yourName”的值
angular.js的變數都放在大括號中
像上圖的 {{yourName || ‘everyone’}}
意思是「如果“yourName”有值就印出來,否則就印字串“everyone”
本範例原始檔:

Gmail SMTP登入被擋

用戶端不接受我的使用者名稱和密碼

如果系統重複提示您輸入使用者名稱和密碼,或顯示「無效的憑證」或「必須登入網路」錯誤,請確認您輸入的是正確的密碼。提醒您,輸入密碼時必須區分大小寫。

如果您確認密碼無誤,請嘗試按照下列疑難排解提示進行:

ssmtp with Ubuntu 12.04

[PHP]ubuntu上的PHP的mail()

因為php可以用mail()寄信,但要寄至外部,要先做些設定,還要裝mail server,這邊是使用google伺服器來幫我們寄信,所以要先有個google mail的帳號,假設google mail的帳號是test@gmail.com密碼是googlepass,設定如下:

1: Install ssmtp

1
sudo apt-get install ssmtp

Check where the binary and the ssmtp.conf file ended up. You’ll need to know the locations for the following steps:

1
2
francis@francis-laptop:~$ whereis ssmtp
ssmtp: /usr/sbin/ssmtp /etc/ssmtp /usr/share/man/man8/ssmtp.8.gz

2. Configure ssmtp

Edit /etc/ssmtp/ssmtp.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids &lt; 1000
# Make this empty to disable rewriting.
root=test@gmail.com

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=smtp.gmail.com:587

# Where will the mail seem to come from?
#rewriteDomain=

# The full hostname
hostname=test@gmail.com

# Are users allowed to set their own From: address?
# YES – Allow the user to specify their own From: address
# NO – Use the system generated From: address
FromLineOverride=YES

UseSTARTTLS=YES
AuthUser=test@gmail.com
AuthPass=googlepass

3. Setup your users

Edit /etc/ssmtp/revaliases

You’ll probably want to set up your local user and root for sedning mail:

1
2
root:test@gmail.com:smtp.gmail.com:587
localusername:test@gmail.com:smtp.gmail.com:587

4. Configure PHP

Edit /etc/php5/apache2/php.ini

(This is correct for Ubuntu 10.04. You can always check which php.ini file Apache is using with the PHP function call phpinfo( INFO_GENERAL ).)

Find the sendmail configuration line:

1
2
3
sudo grep -ni sendmail_path /etc/php5/apache2/php.ini

1047:;sendmail_path =

and change to

1
sendmail_path = /usr/sbin/ssmtp -t

Restart Apache

1
sudo service apache2 restart

(Check for other distributions. If you’re completely lost, a reboot will do it!)

5. Send a mail

1
2
3
4
5
6
<?php

$to="somebody@gmail.com";
$subject="test";
$msg="smtp testing";
$headers="From: test@gmail.com“;
echo “test";
if(mail(“$to","$subject", “$msg", “$headers"))
        echo “succeed";
else
        echo “failed";


?>

and you’re done!

How I configured sendmail for PHP on Ubuntu Server 12.04

Preventing sendmail from been very slow

The first thing that I did after installing sendmail with

aptitude install sendmail

is I put “gate.localhost” (gate is my server name) to /etc/hosts so it looks like this:

127.0.0.1       localhost.localdomain localhost
127.0.1.1       gate.localhost gate

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

With default /etc/hosts containing only “gate” and “localhostsendmail hangs up for a while and writes to/var/log/mail.log the following message:

My unqualified host name (gate) unknown; sleeping for retry.

Testing

To test how sendmail works I used the following command:

/usr/sbin/sendmail -t -f myaddress@gmail.com -v -i < body.txt

Where body.txt is a text file containing the message body:

Subject: test mail to Some Address!
From: Me <fromaddress@somemail.com>
To: Vasya<toaddress@somemail.com>
first line of my message

Please note, that without –f  option provided in command line sendmail would not work, saying that there are some problems with the sender address.

Configuring SMTP server settings

To make sendmail work via my SMTP server I added SMART_HOST directive to sendmail.mc:

define(`SMART_HOST',`smtp.spbtlg.ru')dnl

Then I executed the following commands to changes take effect:

cd /etc/mail
m4 sendmail.mc > sendmail.cf
make
/etc/init.d/sendmail restart

Fortunately, my SMTP server does not require any authorization so I do not need to use AuthInfo directive.

Configuring PHP mail() function

At last I put the following to /etc/php5/apache2/php.ini:

; For Unix only.  You may supply arguments as well (default: "sendmail -t -i").
; http://php.net/sendmail-path
sendmail_path = "/usr/sbin/sendmail -t -f myaddress@gmail.com -i"

Conclusion

I feel that there is another way, but looks like my php mail() function now works fine, at least I can receive messages from my websites.