digits = datasets.load_digits()
n_samples = len(digits.images)
X = digits.images.reshape((n_samples, -1))
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.5, random_state=0)
tuned_parameters = [{'kernel': ['rbf'], 'gamma': [1e-3, 1e-4],
'C': [1, 10, 100, 1000]},
{'kernel': ['linear'], 'C': [1, 10, 100, 1000]}]
scores = ['precision', 'recall']
for score in scores:
clf = GridSearchCV(SVC(), tuned_parameters, cv=5, scoring='%s_macro' % score)
clf.fit(X_train, y_train)
print("Best parameters set found on development set:", clf.best_params_)
means = clf.cv_results_['mean_test_score']
stds = clf.cv_results_['std_test_score'],
for mean, std, params in zip(means, stds, clf.cv_results_['params']):
print("%0.3f (+/-%0.03f) for %r" % (mean, std * 2, params))
y_true, y_pred = y_test, clf.predict(X_test)
print(classification_report(y_true, y_pred))
The simplified version of Parity Wallet Hack 1 is as follows:
The Wallet contract is a simple contract that uses delegatecall to execute transactions using WalletLibrary ’s code within the context of the Wallet function. The WalletLibrary contract assumes that it will be called in the context of a contract that does have state that it can modify (namely m_numOwners) and this is the core assumption that caused the recent disaster. In the first Parity Wallet Hack, the hacker changed the state of various Wallet contracts by delegating a call to initWallet, setting themselves as the owner of the Wallet contract and then withdrawing funds normally.
154 miliony dolarów...
The WalletLibrary contract assumes that it will be called in the context of a contract that does have state that it can modify (namely m_numOwners) and this is the core assumption that caused the recent disaster.
154 miliony dolarów...
Pozwalają nam:
Definicja:
foo :: Int -> Int
foo x = x + 10
bar :: String -> Int
bar s = length s
Wywołanie:
foo 42
bar "Haskell is love, haskell is life"
Definicja:
data Foo = Foo Int | Bar String
Stworzenie wartości:
foo = Foo 42
bar = Bar "Haskell"
Użycie w funkcji:
someFun :: Foo -> Foo
someFun (Foo x) = Foo (x + 1)
someFun (Bar s) = Bar (s ++ s)
Definicja:
newtype Dollar = Dollar Double
Stworzenie wartości:
amountUsd = Dollar 123.0
Użycie w funkcji:
someFun :: Dollar -> Dollar
someFun (Dollar amt) = Dollar $ amt + 10.0
Let:
let x = 15 in x + 10
foo :: Int -> Foo
foo x =
let s = show (x + 100)
in Bar s
Where:
x + 10 where x = 15
foo :: Int -> Foo
foo x = Bar s
where s = show (x + 100)
Funkcje bez nazwy:
\x -> x + 1
\x y -> x + y
Użycie:
filter (\x -> x `mod` 2 == 1) [1,2,3,4,5]
map (\x -> x + 10) [1,2,3,4,5] -- map (+ 10) [1,2,3,4,5]
Prawdziwy, parametryczny!
foo :: a -> [a]
foo x = [x, x, x]
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)
Typy danych:
data Foo a b = Foo a | Bar b
Funkcje:
Dolar (aplikacja funkcji):
f (g x)
-- to to samo, co:
f $ g x
Bezpunktowość:
f x = g x
-- to to samo, co
f = g
-- natomiast:
foo x = f $ g x
-- to to samo, co:
foo = f . g
Definicja:
class Addable a where
add :: a -> a -> a
zero :: a
Instancja:
reduce :: Addable a => [a] -> a
reduce = foldl add zero
Wykorzystanie w funkcji:
instance Addable Foo where
add (Foo x) (Foo y) = Foo $ x + y
add (Foo x) (Bar s) = Bar $ show x <> s
add (Bar s) (Bar z) = Bar $ s <> z
zero = Foo 0
Ulepszmy nieco naszą typeclassę!
class Addable a b where
type Res a b
add :: a -> b -> Res a b
Instancja:
data Foo = Foo Int
data Bar = Bar String
instance Addable Foo Bar where
type Res Foo Bar = Bar
add (Foo x) (Bar s) = Bar $ show x <> s
instance Addable Bar Foo where
type Res Bar Foo = Bar
add f b = add b f
instance Addable Foo Foo where
type Res Foo Foo = Foo
add (Foo x) (Foo y) = Foo $ x + y
instance Addable Bar Bar where
type Res Bar Bar = Bar
add (Bar s) (Bar z) = Bar $ s <> z
Potrzebny tylko jeden tutorial:
W dużym skrócie:
someFunc :: Int -> IO Int
someFunc x = do
let y = x + 10
line <- getLine
pure $ y + length line
f :: Int -> Int
f <$> someFunc 10
g :: IO Int
g >>= someFunc
h :: Int -> IO Int
h >=> someFunc
Kojarzycie identyczność?
id :: forall a. a -> a
id a = a
Taką funkcję można przekazać jako parametr:
poly id. -- działa!
poly (\n -> n + 1) -- nie działa! za mało polimorficzna!
I wtedy musimy podać prawdziwie polimorficzną funkcję:
poly :: (forall a. a -> a) -> Bool
poly f = (f 0 < 1) == f true
ptr :: Ptr Int <- malloc
Da się ładniej?
sizeOf :: Storable a => a -> Int
sizeOf (undefined :: Int) --- bardzo brzydko!
Przyjrzyjmy się bliżej. Weźmy sizeOf:
ptr <- malloc @Int
Zaalokujmy sobie pamięć:
sizeOf' :: forall a. Storable a => a -> Int
sizeOf' = sizeOf (undefined :: @a)
sizeOf' @Int -- dużo lepiej!
Naprawmy to.
There's no such thing as a strongly typed language
M. Snoyman
data BankState = BankState
{ transactions :: [(String, String, Double)]
, conversionRates :: Map (String, String) Double
} deriving Show
wireMoney :: String -> String -> Double -> String -> BankState -> BankState
wireMoney fromAddr toAddr amount currency oldState = newState
where transaction = (fromAddr, toAddr, amountUSD)
transactions' = transaction : transactions oldState
conversionRates' = conversionRates oldState
amountUSD = convert currency "USD" amount oldState
newState = BankState transactions' conversionRates'
Problem 1: nie o takiego Haskella walczyłem.
Rozwiązanie: użyjmy Lensów.
data BankState = BankState
{ _transactions :: [(String, String, Double)]
, _conversionRates :: Map (String, String) Double
} deriving Show
makeLenses ''BankState
wireMoney :: String -> String -> Double -> String -> BankState -> BankState
wireMoney fromAddr toAddr amount currency oldState =
oldState & transactions %~ (transaction :)
where transaction = (fromAddr, toAddr, amountUSD)
amountUSD = convert currency "USD" amount oldState
Problem 2: to nie jest type safe.
Rozwiązanie: użyjmy newtype'ów!
newtype FromAddr = FromAddr String deriving Show
newtype ToAddr = ToAddr String deriving Show
data BankState = BankState
{ _transactions :: [(FromAddr, ToAddr, Double)]
, _conversionRates :: Map (String, String) Double
} deriving Show
makeLenses ''BankState
wireMoney :: FromAddr -> ToAddr -> Double -> String -> BankState -> BankState
wireMoney fromAddr toAddr amount currency oldState =
oldState & transactions %~ (transaction :)
where transaction = (fromAddr, toAddr, amountUSD)
amountUSD = convert currency "USD" amount oldState
Problem 3: konwersja walut dalej nie jest type safe.
Rozwiązanie: użyjmy typeclass!
Lepiej! Nie pomylimy nadawcy z odbiorcą.
convert :: String -> String -> Double -> BankState -> Double
convert fromCurr toCurr amount bankState = rate * amount
where rates = bankState ^. conversionRates
rate = rates ! (fromCurr, toCurr)
NAJ-GO-RZEJ.
Co się dzieje, gdy danej pary walut nie ma w mapie?
newtype Dollar = Dollar Double deriving Show
newtype Zloty = Zloty Double deriving Show
class Convertible a b | a -> b where
convert :: a -> b
instance Convertible Dollar Zloty where
convert (Dollar d) = Zloty $ d * 3.64
instance Convertible Zloty Dollar where
convert (Zloty z) = Dollar $ z * 0.27
Teraz konwersje są definiowane statycznie, nie będzie błędu w runtime'ie.
data BankState = BankState
{ _transactions :: [(FromAddr, ToAddr, Dollar)]
} deriving Show
makeLenses ''BankState
wireMoney :: Convertible a Dollar
=> FromAddr -> ToAddr -> a -> BankState -> BankState
wireMoney fromAddr toAddr amount = transactions %~ (transaction :)
where transaction = (fromAddr, toAddr, amountUSD)
amountUSD = convert amount
I tutaj już wygląda to dużo lepiej.
Jeszcze kosmetyka.
data Transaction a = Transaction
{ _fromAddr :: FromAddr
, _toAddr :: ToAddr
, _amount :: a
} deriving Show
makeLenses ''Transaction
newtype Ledger = Ledger
{ _transactions :: [Transaction Dollar]
} deriving Show
makeLenses ''Ledger
type BankState a = StateT Ledger IO a
emptyState :: Ledger
emptyState = Ledger []
wireMoney :: (Convertible a Dollar, MonadState Ledger m, MonadIO m)
=> Transaction a -> m ()
wireMoney t = do
let t' = t & amount %~ convert
State.modify $ transactions %~ (t' :)
runBank :: BankState () -> IO Ledger
runBank = flip State.execStateT emptyState
Bank5.runBank $ do
let from = Bank5.FromAddr "from"
to = Bank5.ToAddr "to"
trans = Bank5.Transaction from to (Bank5.Zloty 123.0)
Bank5.wireMoney trans
Ładnie?