Haskell — 你应该学的一门语言


前沿

三月的时候立下一个flag, 说是在一个月内学会haskell,现在已经8月了,终于有时间坐下来好好地看看haskell,一直以来我都执着于各种语言,现在已经掌握的语言包括: go, python, php, c, java, javascript, shell, 这些语言在不同的领域都是神兵利器,能够帮我解决不同的问题,但是haskell不一样,是一种我完全凭借兴趣去学习的语言。

刚开始看趣学指南的时候,觉得这门语言语法太奇怪了,我看得很难受。慢慢发现这其实是一种思维定势,如果我的第一门入门语言是haskell的话,想必就不是这种态度了。 Haskell是一门非常迷人的语言,它的列表推导式真的很厉害,能够解决原来传统过程式语言需要写很多代码才能解决的问题。它给了你另外一种思考问题的方式,开阔视野。

我觉得既然是计算机从业者,都应该去学学python和haskell两门语言,python将教会你什么如何让世界变美好,Haskell将告诉你这个世界是多么奇妙。如果你现在被冯·诺依曼式的架构侵染太深的话,学学Haskell吧,它会告诉你这世界上还有这样写代码的方式。

书籍

最棒的haskell免费入门书 haskell趣学指南

视频

下面是两小时入门haskell的内容,视频我也贴在下面了,但直接访问不了,你懂的。

讲义

下面是我翻译过的视频随堂讲义内容:

-- Haskell 是一种函数式编程语言
-- 在Haskell 中所有的值都是immutable 的,所以一旦一个变量被赋值之后,它就不会改变了
-- 函数可以作为另一个函数的参数
-- 递归函数在hankell中很普遍
-- Haskell没有for.while,以及典型的变量,但是它拥有常量
-- Haskell试试一种惰性求值的语言,只有在真正需要的时候再进行求值,以及错误检查


-- 最佳的Best free book
-- http://learnyouahaskell.com/chapters


-- 输入 ghci 到你的terminal中


import Data.List
import System.IO

-- This is haskell comment single line

{-

muti-line comment
-}


-- --------DATA types ---------
-- Haskell 能够自己进行类型推定
-- Haskell 是一种静态类型语言,在编译之后无法改变类型
-- 值不可变(immutable)
-- 你可以在gchi 中使用 :t 来查看数据类型

-- Int : 所有的数字范围在 -2^63 ~ 2^63
-- :: Int 表示这个类型是一个Int类型的数据

maxInt = maxBound :: Int
minInt = minBount :: Int

-- Integer 无限制的number类型

-- Float: 单精度浮点数
-- Double: 双精度浮点数
bigFloat = 3.99999999999+0.00000000005 

-- Bool: True或者False
-- Char: 一个unicode字符,用单引号括起来
-- Tuple: 能够存储多种数据类型的一组数据 (11 pts 精度)

-- 你可以用这种方式声明一个常量

always5 :: Int
always5 = 5


-- ------MATH-----
-- 一些牛逼的操作
-- 求1到100的和

sumofValues = sum[1..1000]

addEx = 5 + 4
subEx = 5 - 4
multEx = 5 * 4
divEX = 5 / 4

-- mod 是前缀操作符

modEx = mod 5 4

-- 你可以通过反引号将前缀函数变为中缀形式
modEx2 = 5 `mod` 4

-- 负数必须要用括号括起来

negNumEx = 5 + (-4)

-- 如果你定义了一个Int类型的数,你必须要用fromIntegral 函数先处理之后才能够使用sqrt函数进行处理
num9 = 9 :: Int
sqrtof9 = sqrt (fromIntegral 9)

-- built in 的一些数学函数
piVal = pi
ePow9 = exp 9
logOf9 = log 9
squared9 = 9 ** 2
truncateVal = truncate 9.999
roundVal = round 9.999
ceilingVal = ceiling 9.999
floorVal = floor 9.999

-- 当然还有一些基本数学函数: sin cos tan asin acos sinh tanh cosh asinh atanh acosh

trueAndFalse =   True && False
trueOrFalse  = True || False
notTrue = not(True)

-- 记住你可以用 :t 在ghci得到数据的类型
-- 当然也可以在函数中用:t 判断数据的类型

-- :t(+) = Num => a -> a -> a
-- Type a 是一种 num的类型, 传入两个a 类型的数据,然后得到一个a类型的数据

-- :t truncate = (RealFrac a, Integral b) => a -> b

-- ------ LIST ------

-- List是一个单向链表,只能够在前面添加数据
-- List能够存储一系列相同类型的数据

primeNumbers = [3,5,7,11]

-- 在连接两个链表的时候会如果其中一个链表很大会导致连接速度很慢
morePrime = primeNumbers ++ [13,17,19,23,29]

-- 你可以使用冒号 : 进行数据和列表连接, 一定要有一个列表,哪怕是空列表

favNums = 2 : 7 : 21 : 66 : []

-- 你可以弄一个列表的列表

multiList [[3,5,7],[11,13,17]]

-- 在列表面快速添加一个元素 
morePrime2 = 2 : morePrime2

-- 得到列表的长度

length morePrime

-- 得到index 为1 的元素
morePrime !! 1

-- 得到第一个元素
head morePrime
-- 得到最后一个元素
last morePrime

-- 得到除了第一个元素之外的所有元素
tail morePrime

-- 得到除了最后一个元素之外的所有元素

init morePrime

-- 得到指定个数的列表前几个元素
take 3 morePrime

-- 删除前几个元素
drop 2 morePrime

-- 判断一个元素是否在列表中

3 `elem` morePrime

-- or 
 elem 3 morePrime

-- 列表中的最大值

 maximum morePrime
-- 列表中的最小值
 minimin morePrime


-- 列表求和

sum morePrime

-- 列表求积

product morePrime

-- 列表自动推导
zeroToten = [0..10]

-- 步进2列表自动推导

step2list =[2,4..20]

-- 字符列表自动推导
letterlist =['A'..'Z']

-- 字符列表步进2自动推导
letterlist =['A','C'..'Z']

--无限列表,Haskell只计算你所需要的值
infinPow10 = [10,20..]

-- repeat 可以得到一个重复值的列表

many2s = take 10 (repeat 2)

-- replicate 能够指定重复次数和值
-- 3 重复10次
many3s = replicate 10 3

-- 循环列表,无限循环

cyclelis = take 10 (cycle [1,2,3,4,5])

-- haskell 较为高级的列表推导式
-- haskell 可以通过列表推导式实现高级条件数据筛选

-- 将1..10 的元素都乘以2然后返回一个列表
 listtime2 = [x * 2 | x <- [1..10]]
-- 输出 [2,4,6,8,10,12,14,16,18,20]

-- 将1..20中 3*x 小于50的剔除,输出剩下的3*x的列表
 listtime3 = [x * 3 | x <- [1..20], x * 3 < 50]
-- 输出 [3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48]

-- 输出在 1..500中所有能够被 13和9整除的数

divisBy9N13 = [x | x <- [1..500],x `mod` 13 == 0, x `mod` 9 == 0]
-- 输出[117,234,351,468]

-- 列表排序
-- 需要import Data.List
sortedList = sort [9,8,1,2,3,4,7,6]

-- zipWith 可以合并两个列表
 sumOfLists = zipWith (+) [1,2,3,4,5] [6,7,8,9,10]
 -- 输出[7,9,11,13,15]

 --数据过滤,通过filter函数,能够保留满足条件的数据
listBiggerThan5 = filter (>5) sumOfLists

-- takeWhile 能够取出数据直到条件为false

evenUp20 =  takeWhile (<=20) [2,4..]

-- foldl 从左到右应用运算符
-- foldr 从右到左应用运算符
foldl (*) 1 [1,2,3,4]
-- 24


-- ------TUPLES ------
-- Tuple 能够存储多种类型,但是其大小是固定的
randTuple = (1,"Random tuple")

bobsSmith = ("Bob Smith",52)

-- Get the first value
bobsAge = fst bobsSmith
-- Get the second value
bobsAge = snd bobsSmith

-- zip函数能够将两个列表压缩为tuples

names =["Bob","Mary","Tom"]
address =["123 Main","234 North","567 south"]

nameNaddress = zip names address
-- 输出 [("Bob","123 Main"),("Mary","234 North"),("Tom","567 south")]

-- ------ FUNCTIONS -------
-- ghc --make haskelltut 将会编译你的程序并执行main函数

-- Functions 必须由小写字母开始

-- 我们能够使用let关键字定义一个函数
-- let num7 = 7
-- let getTriple x = x * 3
-- getTriple num7 = 21

-- main 函数能够在terminal中被调用的main函数

main = do
    -- 在一行中打印
    putStrLn "What's your name"
    -- 获取用户的输入并将其存储到name中
    -- <- 运算符能从IO操作中取得数据并放入变量中
    name <- getLine

    putStrLn ("hello" ++ name)

-- 创建一个名为addMe 的函数
-- x 是一个参数,然后后面是类型签名
-- 传入的类型如果符合要求将会自动应用函数声明
-- 所有的函数都要求有返回值
-- 如果一个函数没有参数则称为一个定义或一个名称

--你可以通过如下的形式定义一个函数:
-- funcName :: param1 -> param2 -> returnType
addMe :: Int -> Int -> Int

-- funcName param1 param2 = operations (Returned Value)
-- 执行: addMe 4 5
addMe x y = x + y

-- 如果没有类型声明则该函数能够处理浮点数
sumMe x y = x + y


-- 当然你也可以定义一个tuple相加函数
addTuples :: (Int,Int) -> (Int,Int) -> (Int,Int)
addTuples (x1,y1) (x2,y2) = (x1 + x2, y1 +y2)

-- 你可以根据不同的值进行不同的操作 类似于switch case
whatAge :: Int -> String
whatAge 16 = "You can drive"
whatAge 18 = "You can vote"
whatAge 21 = "You are a adult"
-- default
whatAge x = "Nothing Import"

-- 定义一个我们期望输入以及输出的函数
factorial :: Int -> Int
-- 如果是0 则返回1 (递归函数)
factorial 0 = 1
factorial n = factorial(n -1)

-- 3 * factorial (2) : 6
-- 2 * factorial (1) : 2
-- 1 * factorial (0) : 1

-- 当然你也可以定义一个乘法的Factorial
productFactorial n = product [1..n]

-- 我们能够利用竖线来根据不同情况下的返回值
isOdd :: Int -> Bool

isOdd n
    -- if 如果是奇数
    | n `mod` 2 == 0 = False
    -- else
    | otherwise = True

-- 当然这个函数能够精简
isEven n = n `mod` 2 == 0

-- 多个条件的 if else 

    whatGrade :: Int -> String
    whatGrade age
        | (age >= 5) && (age <= 6) = "Kindergarten"
        | (age >= 6) && (age <= 10) = "Elementary school"
        | (age >= 10) && (age <= 14) = "Middle school"
        | (age >= 14) && (age <= 18) = "High school"
        |  otherwise "Go to college"

-- 使用where能够帮我们处理条件
batAvgRating :: Double -> Double -> String
batAvgRating hits atBats
    | avg <= 0.200 = "Terrible Batting Average"
    | avg <= 0.250 = "Average Player"
    | avg <= 0.280 = "Your doing pretty good"
    | otherwise = "You're a Superstar"
    where avg = hits / atBats

-- 多条件判断不同的list状态
getListItems :: [Int] -> String
getListItems [] = "Your list is empty"
getListItems (x:[]) = "Your list contains " ++ show x
getListItems (x:y:[]) = "Your list contains " ++ show x ++ " and " ++ show y
getListItems (x:xs) = "The first item is " ++ show x ++ " and the rest are " 
    ++ show xs

-- 我们也能够通过模式来定义一个函数
getFirstItem :: String -> String
getFirstItem [] = "Empty String"
getFirstItem [email protected](x:xs) = "The first letter in " ++ all ++ "is" ++ [x]



-- ------ 高阶函数 -------
-- 能够将一个函数像一个值一样传入到另一个函数中

times4 :: Int -> Int
times4 x = x * 4

-- map 能够将一个列表应用于另一个函数并求值

listTimes4 = map times4 [1,2,3,4,5]
-- [4,8,12,16,20]

-- 然后我们来定义一个map

multBy4 :: [Int] -> [Int]
multBy4 [] = []

-- 将一个列表中的某个值取出然后乘以4再存入到另一个列表中
-- 这里的xs 类型是 [Int], 这是一个递归写法 
multBy4 (x:xs) = times4 x : multBy4 xs

-- 判断两个字符串是否相等

areStringEq :: [Char] -> [Char] -> Bool
areStringEq [] [] = True
areStringEq (x:xs) (y:ys) = x == y && areStringEq xs ys
areStringEq _ _ = False

-- 将一个函数作为另一个函数的参数
-- (Int -> Int) 代表我们需要一个参数是Int 返回值是Int的函数作为参数传入

doMult :: (Int -> Int) -> Int
doMult func = func 3

num3Time4 = doMult times4

-- 返回一个函数
getAddFunc :: Int -> (Int -> Int)

-- 我们能够值传入
getAddFunc x y = x + y

-- 我们也能得到一个+3的函数

adds3 = getAddFunc 3
fourPlus3 = adds3 4

-- 我们也能够将这个函数用到map中

threePlusList = map adds3 [1,2,3,4,5]


-- ------ LAMBDA ------
-- 创建一个匿名函数 也称为lambda 用 \ 开始表示这是一个lambda (\arguments -> result)

db1To10 map (\x -> x * 2) [1..10]

-- ------- 条件 ------
-- 所有的if 都必须包含else

doubleEvenNumber y = 
    if (y `mod` 2 /= 0)
        then y
        else y * 2

-- 我们能够利用case表达式
getClass :: Int -> String
getClass n = case n of 
    5 -> "Go to kindergarten"
    6 -> "Go to elementary school"
    _ -> "Go to some place else"


-- ------ MODULES -------
-- 我们能够将一组函数组织起来,集合成一个module,通过import能加载一个模块
-- 那么如何创建一个模块呢?
-- 1. 创建一个文件
-- 2. 将所有需要使用的函数包含进去
-- TODO
-- 3.  在文件的顶部将所有需要导出的函数列出

-- ------ 枚举 ------

data BaseallPlayer = Pitcher
                    | Catcher
                    | Infield
                    | Outfield
                deriving Show

barryBonds :: BaseallPlayer -> Bool
barryBonds Outfield = True

barryInOf print(barryBonds Outfield)

-- ------ 自定义类型 ------

data Customer = Customer String String Double
    deriving Show

--定义自定义类型的变量
tomSmith :: Customer
tomSmith = Customer "Tom Smith" "123 Main st" 20.50

-- 定义一个需要使用该自定义类型的函数
getBalance :: Customer -> Double
getBalance (Customer _ _ b) = b

tomSmithBal print(getBalance tomSmith)


data RPS = Rock | Paper | Scissors

shoot :: RPS -> RPS -> String
shoot Paper Rock = "Paper Beats Rock"
shoot Rock Scissors= "Rock Beats Scissors"
shoot Scissors Paper= "Rock Beats Scissors"
shoot Scissors Rocks= "Scissors Lose to Rocks"
shoot Paper Scissors= "Paper Lose to Scissors"
shoot Rock Paper= "Rock Loses to Paper"
shoot _ _= "Error"

data Shape = Circle Float Float Float | Rectangle Float Float Float Float
    deriving (Show)

-- :t Circle = Float -> Float -> Float -> Float

area :: Shape -> Float
area (Circle _ _ r) = pi * r ^ 2 
area (Rectangle x y x2 y2) = (abs (x2 - x))  * (abs (y2 - y))

-- 使用 $ 符号可以把$后面的表达式作为一个参数传入到前面的函数中
-- 使用 . 可以作为一个管道符号,将后面一个函数的输出作为前一个函数的输入
putStrLn . show $ 1 + 2

-- Get area of shapes 
areaOfCircle = area (Circle 50 60 20)

areaOfRectangle = area $ Rectangle 10 10 100 100

-- ------ Type Class -------
-- Num, Eq 和 Show 是类型类
-- Type Class相当于面向对象的接口
-- 多态函数(具有多种参数,多种数据类型,通常用类型定义
-- 例如, + 运算符就是用Num 类型类定义的
-- :t (+) :: Num a => a -> a -> a
-- 这代表 a 是Num的一个实例,+ 能够处理两个a类型的数然后返回一个类型为Num的数

--自定义一个自定义类型类,并让其能够进行比较

data Emplyee = Emplyee {
    name :: String,
    position :: String,
    idNum :: Int,
} deriving (Eq,Show)

samSmith = Emplyee{name = "Sam Smith",position = "Manager",IdNum = 1000}
pamMax = Emplyee{name = "Pam Max",position = "Sales",IdNum = 1001}

isSamPam = smaSmith == pamMax

samSimithData = show smaSmith

-- 实现一个自己实现的Eq 和Show类型类的数据类型
data ShirtSize = S | M | L

instance Eq ShirtSize where
    S == S = True
    M == M = True
    L == L = True
    _ == _ = False

instance Show ShirtSize where
    show S = "Small"
    show M = "Medium"
    show L = "Large"

-- Check if S is in the list
-- 需要实现Eq才能够用elem 方法
smallAvail = S `elem` [S, M, L]

-- Get string value for ShirtSize
-- 需要实现Show 类型类
theSize = show S

-- 自定义类型类
class MyEq a where
    areEqual :: a -> a -> Bool


-- 实现自己定义的类型类

instance MyEq ShirtSize where
    areEqual S S = True
    areEqual M M = True
    areEqual L L = True
    areEqual _ _ = Fasle

newSize = areEqual M M




-- ------ I/O ------

sayHello = do
    putStrLn "What's your name: "
    name <- getLine

    putStrLn $ "Hello" ++ name

-- File I/O

writeToFile = do
    theFile <- openFile "test.txt" WriteMode

    hPutStrLn theFile ("Random line of text")

    hClose theFile

readFromFile = do
    theFile2 <- openFile "test.txt" ReadMode

    contents <- hGetContents theFile2
    putStr contents
    hClose theFile2


-- ------ EXAMPLE: FIBONACCI SEQUENCE ------
-- 1,1,2,3,5,8, ...

-- 1 : 1 : 代表函数的开始
-- | 表示对于每个 a,b 并将其相加
-- <- 存储 a + b 之和
-- tail : 得到除了第一个值之外的所有值
-- zip 将两个列表压缩,并得到一个tuple 列表

fib = 1 : 1: [a + b | (a , b) <- zip fib (tail fib)]
-- 对上式进行推演
-- 1. 首先 fib = 1 然后tail fib = 1
-- 2. 现在列表变成了 [1,1,2] 因为 a:1 + b:1 = 2
-- 3. 然后第二步, fib = 1 然后 (tail fib) = 2
-- 4. 现在列表变成了 [1,1,2,3] 因为 a: 1 + b: 2 = 3

《全民目击》的英文片名更贴切


《全民目击》的英文片名更贴切

背景

抽了下午的两个小时看完了朋友强烈推荐的《全民目击》英文名《Silent Witness》。全片看下来可以说是导演已经创意非凡了,通过控辨双方的不同视角互换得到的迥然不同的剧情发展着实是我第一次看到,不禁拍案。看这个电影的时候我有一种意识想到了前段时间也挺火的《嫌疑人X的献身》,又是一部温情献身剧情,最后父爱的升华,讲真剧情设计还是有可圈可点之处的。比某些国产全剧都是尿点的电影高得不知道哪里去了。

评价

说说自己对各个演员的看法吧:

郭富城:其实我看《寒战》系列的时候就觉得他挺适合演这种片子,但是有他在的电影总有一股浓浓的香港警匪片的气息,不知道是不是中毒太深,在庭审的时候一些愤怒和焦灼的情绪表现得很到位,但是在与片中的老婆(因为有戒指)沟通的时候觉得有些僵硬,比如他们坐在一起吃饭的时候,莫名其妙的争吵,莫名其妙的结束,不是很自然,有点牵强。

孙红雷: 两个字,很棒。把一个老谋深算的奸商表现得很好,很切合剧中角色的性格,不得不说,孙红雷的演技我是一直觉得很棒的。
他可以把一些细节表现得很好,比如让自己的女儿不要哭。

亦或是在回答庭审问题时候的淡定自若的表情,都表现的很符合当时的情景。

不得不说,孙红雷在片中饰演的林泰确实是一个很饱满的角色。

余男:再说说余男,演一个女强人的确挺适合她,她的造型能够体现出一种倔强,一种骄傲的感觉出来,但我个人觉得这个角色演的有点过于男性化。
特别是庭审的时候,她的表情很淡定,也很冷漠,我不知道为什么导演要选择她来演,可能和后面剧情的变化,以及一个律师的内心的变化比较贴合,毕竟女人内心更偏向于柔软。一个女强人形象后面有温柔的一面也符合逻辑。
可是我要说的是,她全剧毫无亮点啊,谁演都可以啊。

邓家佳:一直给我的印象就是爱情公寓的小姨妈,其实可以说演技还算可以,和一线影星还是有点差距,比如说在剧中的爸爸说开车撞人的是自己的时候,她的表现只能说是还行吧,有点僵,但是那个眼泪流的我觉得很不错。

赵立新: 这个演员之前没有听说过,这部剧里其实表现得很好。把丰富的情感变化表现的很充足,从刚刚开始的淡定,到被律师激怒之后的失控,再到失误说出自己的秘密之后的恐惧与慌张,不得不说,演的还是不错的,但是不知道为什么后面在警察局里配审讯的时候就一言不发了,这就很尴尬,没什么表现了啊。

主要角色都已经提及,下面说说一些我想说的人:

谭松韵:知道谭松韵我可以说是一段故事了,但是这不是重点。重点是,谭松韵的造型我觉得很是诡异啊。
这造型估计也没sei了,我觉得她还是比较适合清纯造型,毕竟短发配她的脸型,再加上她的眼妆,简直不要太不和谐好不好。

其实作为一部剧情片,我觉得剧本已经写得很不错了,毕竟能够做到缓缓相扣,出人意料又在清理之中,能够把整个主线故事说圆了,如果你不去细细揣摩,应该是问题不大的,确实是大部分无尿点,除了结尾的一点鸡肋剧情之外。细细推敲这部剧还是有些bug的:

  • 在开头的导播穿插播放不知道是和用意,是否是为了艺术效果。
  • 在证人等候室的那个女的为什么要问那么多问题,问了之后就没有然后了,是不是为了做什么铺垫,但是后面再没有出现那个女的的戏份了,这就有点尴尬了。
  • 郭富城的“老婆”的出现是为了渲染情感吗?如果是的话,好像也并没有起到很好的作用,为了塑造一个郭富城铁面无私的形象?
  • 在监控录像中,为什么看不到林泰下车的画面,感觉说不通。
  • 林家司机作为一名15年的老司机,我觉得完全可以认出车的型号和来源,如果是杨丹(剧中受害者)受害,稍加一想完全可以知道这是自家人,如果按剧中逻辑,报警可能性几乎不存在。
  • 鸡肋的结尾不知道是为了干什么,多了又觉得没什么卵用,去掉么又觉得好像也不太好。

总结

总体而言,这部电影是值豆瓣的7.6分的,特殊的叙事手法,挺好的剧情,以及孙红雷和郭富城的搭档都值得你花两个小时看完这部电影。而且这部13年的电影在各大视频网站上都是免费的,推荐大家去看看。然后我想说的是,这个和“全民目击”也没有半毛钱关系吧,反而我觉得“Slient Witness”反而更适合这部电影。

matering sed —— 精通sed


mastering sed

我承认这是标题党,但是sed在平时工作中sed真的很有用,特别是像我这种需要写一些shell进行自动化工作的。

sed 快速入门

sed 作为流编辑神器,在写脚本的时候往往会发挥奇效,所以这里我将sed的一些关键用法介绍一下,目的在于实用,而不是全面。

-i 参数

常常会看到会有sed -i这样的命令,-i的意思就是直接在源文件上进行操作。

动作

sed动作是我们需要掌握的最为重要的内容 :
– a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行);
– c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行;
– d :删除, d 后面通常不接任何字符;
– i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
– p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行;
– s :替换,可以直接进行取代的工作通常这个 s 的动作可以搭配正则语法,语法很像vim的替换。

样例

测试文件内容:

# cat song.txt
my name is zhangshan
his name is lisi
my dog name is doglas
today is good day
we sang a song:
lalalalala
hahahahaha

s 替换

sed "s/zhangshan/wangnima/g" song.txt

替换的方式和vim很像

输出

# sed "s/zhangshan/wangnima/g" song.txt
my name is wangnima
his name is lisi
my dog name is doglas
today is good day
we sang a song:
lalalalala
hahahahaha

这样只会将修改之后的文件输出到标准输出,如果需要修改原来的文件的话,加上-i参数:

sed -i "s/zhangshan/wangnima/g" song.txt

修改就会在源文件上生效

用正则武装sed
正则表达式是一件神兵利器,这里介绍一些较为简单的正则表达式:
^ 表示一行的开头,如:/^my/ 这就是以单词my为开头的匹配
$ 表示一行的结尾,如: /:$/ 就是以字符:为结尾的匹配
\< 表示一个词的开头,如\<s 就是以s开头的词
\> 表示一个词的结尾,如g\> 就是以g为结尾的词
. 表示任意单个字符
* 表示一个字符可以出现0次或者多次
? 表示一个字符可以出现1次或者多次
[] 集合,例如[abc]可以匹配a,或b,或c,经典例子:[a-zA-Z]匹配所有26个字母,^表示取反,如[^a]表示非a的字符。

只替换第一行的内容

sed "1s/name/nume/g"

输出

# sed "1s/name/nume/g" song.txt
my nume is wangnima
his name is lisi
my dog name is doglas
today is good day
we sang a song:
lalalalala
hahahahaha

指定行号范围

sed "1,2s/name/nume/g" song.txt

输出

# sed "1,2s/name/nume/g" song.txt
my nume is wangnima
his nume is lisi
my dog name is doglas
today is good day
we sang a song:
lalalalala
hahahahaha

只替换每行第一个s

sed "s/s/S/1" song.txt

输出

# sed "s/s/S/1" song.txt
my name iS wangnima
hiS name is lisi
my dog name iS doglas
today iS good day
we Sang a song:
lalalalala
hahahahaha

只替换每行第2个s

sed "s/s/S/2" song.txt

输出

# sed "s/s/S/2" song.txt
my name is wangnima
his name iS lisi
my dog name is doglaS
today is good day
we sang a Song:
lalalalala
hahahahaha

只替换每行第2个之后的s

sed "s/s/S/2g" song.txt

输出

# sed "s/s/S/2g" song.txt
my name is wangnima
his name iS liSi
my dog name is doglaS
today is good day
we sang a Song:
lalalalala
hahahahaha

多次匹配

将第一行的”wangnima”改成”zhangshan”然后把第二行的lisi改成”wangwu”

sed "1s/wangnima/zhangshan/g; 2s/lisi/wangwu/" song.txt

输出

# sed "1s/wangnima/zhangshan/g; 2s/lisi/wangwu/" song.txt
my name is zhangshan
his name is wangwu
my dog name is doglas
today is good day
we sang a song:
lalalalala
hahahahaha

等价命令

sed -e "1s/wangnima/zhangshan/g" -e "2s/lisi/wangwu/" song.txt

`&` 的妙用
可以用&表示被匹配到的字符,我们可以用这个trick 做一些事情:

sed "s/name/@&@/g" song.txt

输出:

my @[email protected] is wangnima
his @[email protected] is lisi
my dog @[email protected] is doglas
today is good day
we sang a song:
lalalalala
hahahahaha

圆括号匹配
在正则表达式中,圆括号括起来的正则部分可以作为变量使用,sed中用\1,\2…表示匹配到的值:

sed "s/\([a-z]*\) name is \([a-z]*\)/\1:\2/g" song.txt

输出

# sed "s/\([a-z]*\) name is \([a-z]*\)/\1:\2/g" song.txt
my:wangnima
his:lisi
my dog:doglas
today is good day
we sang a song:
lalalalala
hahahahaha

今天过后,就是最熟悉的陌生人


《明日的我与昨日的你约会 ぼくは明日、昨日のきみとデートする (2016)》影评

上次看这种很纯的爱情片应该是看得午夜场的《君の名は》了,因为之前已经提前看了抢先版,你的名字带给我的感触有点淡化。这部电影真的很催泪,我虽然泪腺不敏感,可是我突发有种很想写点什么的冲动。


我为什么要用“今天过后,就是最熟悉的陌生人”作为标题呢,这不是一部虐心的电影,甚至很甜。整部电影从男女主美丽的邂逅,到相识相知相爱。几乎就是一段完美感情的标准范本。可是,巧妙的剧本把原本非常美好的事情,变得十分虐心,男主过的每一天,都是女主的昨天。所以,男主的每一天,对女主都意味着是过去的一天。男主的第一次牵手,是女主的最后一次牵手,男主的第一次告白,是女主经历的最后一次被告白。甜是最腻的甜,想到这一层,往往心中就有一丝丝遗憾,所谓悲剧,就是要这种令人艳羡的美好所衬托,才是最虐。

小松菜奈饰演的女主福寿爱美,在剧中真的演的很棒,在最美的年龄,最美的微笑,真的很吸引观众。非常成功,作为一部纯爱电影,朴素但是真实的表现,几乎让我觉得自己仿佛还在那个年龄,还在期待那种恋情的错觉。着实代入感很强。


这就是我认为的一部青春爱情片所应该有的东西,最纯的爱情才是所谓的青春,最疯的爱情都是冲动。我实在是欣赏不来国产青春爱情片不离叛逆堕胎的套路。如果非要给自己的青春和爱情留点温存,这样的感觉才是合适的。

我的明天是你的昨天,两个相逆的时间线,让所有的美好都带了一些遗憾和伤感,所有的美好,对对方而言都是没有经历过的未来,爱美每天都要假装经历过那些,她肯定是痛苦的,但是她又是快乐的。他们俩的爱情,从另一个角度讲。似乎在不断地重演,双方的时间线在不断地循环,变成了一个环。我想,其实这也是这个剧本的巧妙之处,也许在未来,双方没有办法在一起,但在最美好的时候,你们相遇了,相爱了,可以按着既定的脚本去爱,每天都有新的感悟,我相信笑出来的那一刻,内心真的是幸福的。


今天过后,我就只能看着你把亲昵的称呼改掉,从一个最熟悉,最深爱的人慢慢变成一个陌生人,这是最虐的地方,这也是,这部电影最感人的地方。

当南山高寿初次和福寿爱美见面的时候,问道:“还能见面吗?”,福寿爱美,想到,明天对自己来说,同南山高寿就是不同时间线的陌生人了,内心痛苦挣扎。可是,她还是微笑地回答道:”当然可以,明天见!”。对于南山高寿而言,明天,是非常美好的一天啊。真是又甜蜜又虐心。

这部电影,我真的很喜欢。10分。