考虑功能
f g h x y = g (g x) (h y)
它的类型是什么?显然我可以使用:t f
来查找,但如果我需要手动推断,那么最好的方法是什么呢?
我所展示的方法是为参数分配类型并从那里推导 - 例如x :: a
,y :: b
为g :: a -> c
,h :: b -> d
提供了c
和d
g x
,h y
,c = a
然后我们继续从那里做出推论(g (g x) (h y)
来自x
等)。
然而,这有时只会变成一个巨大的混乱,我常常不确定如何进一步扣除或在我完成后解决。其他问题有时会发生 - 例如,在这种情况下^([a-zżźćńółęąś]+)\s+-\s+(\d+\.\d+)$
将会变成一个函数,但在作弊和查找类型之前,这对我来说并不明显。
是否有一种特定的算法始终有效(并且人类可以快速执行)?否则,是否有一些我缺少的启发式或技巧?
答案 0 :(得分:9)
让我们检查顶层的功能:
f g h x y = g (g x) (h y)
我们将首先为类型指定名称,然后继续并专门化它们,因为我们会更多地了解函数。
首先,让我们为顶级表达式指定一个类型。我们称之为a
:
g (g x) (h y) :: a
让我们取出第一个参数并分别指定类型:
-- 'expanding' (g (g x)) (h y) :: a
h y :: b
g (g x) :: b -> a
再次
-- 'expanding' g (g x) :: b -> a
g x :: c
g :: c -> b -> a
再次
-- 'expanding' g x :: c
x :: d
g :: d -> c
但请坚持:我们现在拥有g :: c -> b -> a
和g :: d -> c
。因此,通过检查,我们知道c
和d
是等效的(写为c ~ d
),还有c ~ b -> a
。
这可以通过简单地比较我们推断的g
的两种类型来推断。请注意,这是不类型矛盾,因为类型变量通常足以适合它们的等价物。如果我们在某处推断出Int ~ Bool
,那么会会出现矛盾。
所以我们现在总共得到以下信息:(省略一点工作)
y :: e
h :: e -> b
x :: b -> a -- Originally d, applied d ~ b -> a.
g :: (b -> a) -> b -> a -- Originally c -> b -> a, applied c ~ b -> a
这是通过替换每个类型变量的最具体形式来完成的,即将c
和d
替换为更具体的b -> a
。
因此,只需检查哪些参数在哪里,我们就会看到
f :: ((b -> a) -> b -> a) -> (e -> b) -> (b -> a) -> e -> a
GHC证实了这一点。
答案 1 :(得分:5)
这个功能是:
f g h x y = g (g x) (h y)
或更详细:
f g h x y = (g (g x)) (h y)
最初我们假设所有四个参数(g
,h
,x
和y
)都有不同的类型。我们还为函数引入了一个输出类型(这里是t
):
g :: a
h :: b
x :: c
y :: d
f g h x y :: t
但现在我们要进行一些推断。我们看到例如g x
,因此这意味着函数应用程序具有g
函数,x
参数。这意味着g
是一个函数,输入类型为c
,因此我们将g
的类型重新定义为:
g :: a ~ (c -> e)
h :: b
x :: c
y :: d
f g h x y :: t
(此处,代字号~
表示两种类型相同,因此a
与c -> e
)相同。)
由于g
的类型为g :: c -> e
,而x
的类型为c
,因此这意味着函数应用g x
的结果类型为{{ 1}}。
我们看到另一个函数应用程序,g x :: e
作为函数,g
作为参数。所以这意味着g x
的输入类型(g
)应该等于c
的类型(g x
),因此我们知道{ {1}},现在的类型是:
e
现在我们看到一个函数应用程序c ~ e
函数, c ~ e
g :: a ~ (c -> c)
h :: b
x :: c
y :: d
f g h x y :: t
参数。这意味着h
是一个函数,输入类型与y
的类型相同,因此h
的类型为y :: d
,这意味着:
h
最后我们看到一个函数应用程序d -> f
函数, c ~ e
g :: a ~ (c -> c)
h :: b ~ (d -> f)
x :: c
y :: d
f g h x y :: t
参数,这意味着g (g x)
的输出类型应该是一个函数,{{1} }作为输入类型,h y
作为输出类型,这意味着g (g x) :: c
,因此:
f
这意味着,由于t
包含c ~ (f -> t)
, c ~ e
c ~ (f -> t)
g :: a ~ (c -> c) ~ ((f -> t) -> (f -> t))
h :: b ~ (d -> f)
x :: (f -> t)
y :: d
f g h x y :: t
,f
和g
这些参数,因此h
的类型为:
x
答案 2 :(得分:2)
您已经描述了如何操作,但也许您错过了统一步骤。也就是说,有时我们知道两个变量是相同的:
map2 = folium.Map(location=[43.7, -79.4], tiles='cartodbpositron',
zoom_start=11) # set default location on the map
marker_cluster = folium.plugins.MarkerCluster().add_to(map2) # add the
location to the marker cluster
for point in range(len(locationlist)): # loop through the plots
# include popup
popup1 = folium.Popup(df['Condo Address'][point], parse_html=True)
# include icon
icon1 = folium.Icon(color=df['color'][point])
# mark every addresses in the map from the data
folium.Marker(locationlist[point],popup=popup1,icon =
icon1).add_to(marker_cluster)
我们知道x :: a
y :: b
g :: a -> b -- from g x
h :: c -> d -- from h y
a ~ b -- from g (g x)
和a
是相同的,因为我们将b
,g x
传递给b
,期望{{1} }}。所以现在我们用g
替换所有a
,然后继续我们考虑所有子表达式......
关于你的“大混乱”评论,我有几点要说:
答案 3 :(得分:1)
只需记下它们下面的所有实体类型:
f g h x y = g (g x) (h y)
x :: x y :: y
h :: y -> a , h y :: a
g :: x -> b , g x :: b
g :: b -> (a -> t) , x ~ b , b ~ (a -> t)
f :: (x -> b) -> (y -> a) -> x -> y -> t , x ~ b , b ~ (a -> t)
f :: (b -> b) -> (y -> a) -> b -> y -> t , b ~ (a -> t)
-- g h x y
因此f :: ((a -> t) -> (a -> t)) -> (y -> a) -> (a -> t) -> y -> t
。就是这样。
实际上,
~> :t let f g h x y = g (g x) (h y) in f
:: ((t1 -> t) -> t1 -> t) -> (t2 -> t1) -> (t1 -> t) -> t2 -> t
这是这样的:
x
必须有某种类型,我们称之为x
:x :: x
。y
必须有某种类型,我们称之为y
:y :: y
。h y
必须有某种类型,我们称之为a
:h y :: a
。因此h :: y -> a
。g x
必须有某种类型,我们称之为b
:g x :: b
。因此g :: x -> b
。g _ _
必须有某种类型,我们称之为t
。因此g :: b -> a -> t
。g :: b -> (a -> t)
相同。g
的两种类型签名必须统一,即在所涉及的类型变量的某些替换下相同,因为这两个签名描述了相同的实体,g
。x ~ b, b ~ (a -> t)
。这是替代。f
的所有类型的参数,我们知道它会产生g
生成的内容,即t
。所以我们可以写下它的类型(x -> b) -> (y -> a) -> x -> y -> t
。b
替换为x
,然后将a -> t
替换为b
,每次都从替换中删除已删除的类型变量。当然,我们可以选择先将b
替换为x
,最后替换为x ~ (a -> t)
,然后我们最终会使用相同的类型,如果我们总是将更简单的类型替换为更复杂的类型(例如,将b
替换为(a -> t)
,将替换为,反之亦然)。< / p>
简单的步骤,保证结果。