Clyde学习笔记一(Scope)

Scope相关的接口、类都定义在expr这个包中,官方的说明很简单,只有一句话:expr - expression evaluation and symbol binding,但scope这个概念却很重要,可以说是整个clyde框架的核心基石之一。那么它到底是个什么概念,又起到了什么作用呢?首先它是一个接口,在这个接口中,最重要的就是下面这个方法:

 

public <T> T get (String name, Class<T> clazz);
 

 

再看它的注释:

 

Looks up a symbol in this scope,return the mapping for the requested symbol。

 

在这里,symbol指的就是参数name,这个方法的作用就是找到name这个symbol所对应的值,并且这个值的类型是T。

 

接下来我们再来看下这个方法的实现。scope有两个默认的实现类,分别是SimpleScope和DynamicScope。

 

在SimpleScope中的实现:

 

 

    public <T> T get (String name, Class<T> clazz)
    {
        return ScopeUtil.get(this, name, clazz);
    }

 那么具体的逻辑是写在了ScopeUtil中,在ScopeUtil.get方法中,有关键的这句:

 

 

Member member = getScoped(object.getClass()).get(name);
 

这句话的意思是在object这个类中所有用scoped annotation标注过的属性或方法中找到与name对应的那个。

相关代码如下:

 

 

        HashMap<String, Member> members = new HashMap<String, Member>();
        Class<?> sclazz = clazz.getSuperclass();
        if (sclazz != null) {
            members.putAll(getScoped(sclazz));
        }
        // add all scoped fields (stripping off the leading underscore, if present)
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Scoped.class)) {
                field.setAccessible(true);
                members.put(stripUnderscore(field.getName()), field);
            }
        }
        // add all scoped methods
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Scoped.class)) {
                method.setAccessible(true);
                members.put(method.getName(), method);
            }
        }

 可以看到每个类都有一个对应的map,这个map中保存了所用用scoped标注过的属性和方法,并且以属性和方法名作为map的key,这个key就是我们前面所说的symbol,即那个name参数。在这里的整个方法还递归的包括了这个类所有的超类,但是要主要到这里的scoped是个annotation,和我们前面提到的scope接口不是同一回事。每个类对应的map还保存在一个全局的map中作为cache,这样就不用每次都反射查找一遍。

 

 

    /** Cached scoped members. */
    protected static HashMap<Class<?>, HashMap<String, Member>> _scoped = Maps.newHashMap();
 

找到对应的属性或方法之后,ScopeUtil的get方法就返回属性的值或者执行方法的结果,在这里相关代码就不展开了。现在回过头来重新审视一下scopeUtil的get方法,那就是:

 

找到在object这个类中所有用scoped annotation标注过的属性或方法中查找属性或方法名为name的那个,并返回属性的值或方法执行的结果,返回类型为T。在simpleScope中,这个object就是它自身。

 

再来看一下dynamicScope的实现:

 

 

    public <T> T get (String name, Class<T> clazz)
    {
        // first try the dynamic symbols, then the reflective ones
        Object value = (_symbols == null) ? null : _symbols.get(name);
        return clazz.isInstance(value) ? clazz.cast(value) : ScopeUtil.get(_owner, name, clazz);
    }
 

可以看到,在dynamicScope中,这个object不再是它自己,而是_owner。看一下owner的定义:

 

 

    /** The owner of this scope. */
    protected Object _owner;

 这也是SimpleScope和DynamicScope最重要的一个区别。

 

现在我们已经初步了解了scope的概念,那么这个scope有什么具体的作用呢?

在scope接口中还有两个方法,addListener,removeListener。

 

 

    /**
     * Adds a listener for changes in scope.  The listener will be notified when symbols are
     * added or removed and whenever the scope hierarchy changes.
     */
    public void addListener (ScopeUpdateListener listener);

    /**
     * Removes a listener for changes in scope.
     */
    public void removeListener (ScopeUpdateListener listener);
 

 

再来看一下ScopeUpdateListener这个接口,这个接口SimpleScope和DynamicScope都默认实现了,也就是说SimpleScope和DynamicScope都可以作为监听器监听其他的scope,一旦它监听的scope被改变,scopeUpdated方法都会被调用。

 

 

/**
 * Used to notify objects when the scope has been updated.
 */
public interface ScopeUpdateListener
{
    /**
     * Called when the scope has been updated.
     */
    public void scopeUpdated (ScopeEvent event);
}

 

下面是SimpleScope的scopeUpdate方法的实现。

 

 

    public void scopeUpdated (ScopeEvent event)
    {
        ScopeUtil.updateBound(this, _parentScope);
    }

再看一下ScopeUtil的updateBound方法:

 

 

    /**
     * Updates the {@link Bound} fields of the specified object using the provided scope.
     */
    public static void updateBound (Object object, Scope scope)
    {
        for (Field field : getBound(object.getClass())) {
            String name = field.getAnnotation(Bound.class).value();
            if (name.length() == 0) {
                name = stripUnderscore(field.getName());
            }
            @SuppressWarnings("unchecked") Class<Object> type = (Class<Object>)field.getType();
            try {
                field.set(object, resolve(scope, name, field.get(object), type));
            } catch (IllegalAccessException e) {
                log.warning("Error accessing bound field.", "field", field, e);
            }
        }
    }

 这里也是用的反射找到object类中用Bound annotation标注的属性,并且找到这个属性所bound(绑定)的symbol,resolve出这个symbol的值并赋值给这个属性。

 

再看一下ScopeUtil中resolve方法的实现。

 

 

    /**
     * Attempts to resolve the identified symbol in the given scope.  If not found there,
     * searches the parent of that scope, and so on.
     *
     * @return the mapping for the symbol, or <code>defvalue</code> if not found anywhere in the
     * chain.
     */
    public static <T> T resolve (Scope scope, String name, T defvalue, Class<T> clazz)
    {
        // if the name includes a scope qualifier, look for that scope
        int idx = name.indexOf(':');
        if (idx != -1) {
            String qualifier = name.substring(0, idx);
            name = name.substring(idx + 1);
            while (scope != null && !qualifier.equals(scope.getScopeName())) {
                scope = scope.getParentScope();
            }
        }

        // rise up through the scopes looking for the requested symbol
        for (; scope != null; scope = scope.getParentScope()) {
            T value = scope.get(name, clazz);
            if (value != null) {
                return value;
            }
        }

        // no luck; return the default value
        return defvalue;
    }
 

这个方法就是在给定的scope中找到symbol所对应的值,在当前scope中找不到可以递归的在当前scope的parentScope中继续寻找,并且symbol还可以用“:”分隔符指定一个特定的scope。在上面的代码中我们看到了熟悉一句:

 

 

 T value = scope.get(name, clazz);
 

怎么样,到这里整个拼图中的两块已经连在了一起,并且已经隐约看到了整个scope框架它所需要表达的一个意思,那就是:

 

当一个scope有update时,所有监听该scope的其他scope所拥有的object中,任何绑定到该scope中symbol的属性值都会随着该symbol的值变化而变化。这也正是官方介绍中的symbol binding的概念所在。应该说这个表述非常的抽象,但它却是整个clyde框架的基石,框架中的其他部分有非常多的依赖于这个抽象的概念。回过头来我们再一次看到,在SimpleScope中,object还是它自身,而DynamicScope的object仍然还是它的owner。

 

 

    public void scopeUpdated (ScopeEvent event)
    {
        ScopeUtil.updateBound(_owner, _parentScope);
        wasUpdated();
    }
 

每个scope默认的监听对象都是该scope的parentScope。SimpleScope中的构造函数:

 

 

    /**
     * Creates a new simple scope.
     */
    public SimpleScope (Scope parentScope)
    {
        if ((_parentScope = parentScope) != null) {
            _parentScope.addListener(this);
        }
        ......
    }

 

DynamicScope也是如此。

 

下面写一段测试代码来验证一下。

 

 

package com.meidusa.clyde.test;

import com.threerings.expr.Bound;
import com.threerings.expr.DynamicScope;
import com.threerings.expr.Scope;
import com.threerings.expr.Scoped;
import com.threerings.expr.SimpleScope;

public class TestScope {
	
	public static class TestScoped extends DynamicScope{

		public TestScoped(Scope parentScope) {
			super("test name", parentScope);
		}
		
		@Scoped
		protected String scopedString = "before test";

		public String getScopedString() {
			return scopedString;
		}

		public void setScopedString(String scopedString) {
			this.scopedString = scopedString;
		}
		
	}
	
	public static class TestBounded extends SimpleScope{

		public TestBounded(Scope parentScope) {
			super(parentScope);
		}
		
		@Bound("scopedString")
		protected String boundedString;

		public String getBoundedString() {
			return boundedString;
		}

		public void setBoundedString(String boundedString) {
			this.boundedString = boundedString;
		}
		
	}

	public static void main(String[] args){
		TestScoped parent = new TestScoped(null);
		TestBounded child = new TestBounded(parent);
		System.out.println(child.getBoundedString());
		parent.setScopedString("after test");
		parent.wasUpdated();
		System.out.println(child.getBoundedString());
	}
}

 

运行后输出为:

 

 

before test
after test
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 接下来的n行描述了每个男性的次选列表,最后的n行描述了每个女性的次选列表。假设每个人都喜欢别人,即不存在不喜欢的情况。求稳定婚配方案。 稳定婚配的定义:不存在一对男女,他们可以离开自己的配偶并且彼此更喜欢对方。 输入格式: 第一行,一个整数T,表示测试用例的数量。 对于每个测试用例,第一行包含一个整数n,表示男性和女性的数量。 第二行包含n个单词,表示男性的名字。 第三行包含n个单词,表示女性的名字。 接下来的n行,每行包含n个单词,表示男性的首选列表。 接下来的n行,每行包含n个单词,表示女性的首选列表。 接下来的n行,每行包含n个单词,表示男性的次选列表。 接下来的n行,每行包含n个单词,表示女性的次选列表。 输出格式: 对于每个测试用例,输出一个稳定婚配方案。每个方案占一行,格式为“男性名字-女性名字”。 输入样例: 1 3 amy bob clyde AMY BOB CLYDE AMY BOB CLYDE BOB AMY CLYDE AMY BOB CLYDE CLYDE AMY BOB AMY BOB CLYDE AMY BOB CLYDE 输出样例: amy-AMY bob-BOB clyde-CLYDE ### 回答2: C语言稳定婚配问题是一个经典的算法问题,即给定一个男女人数相等的集合,每个人有一个名字和一个倾向性列表,要求找出一种匹配方案,使得每个人都有且只有一个配偶,并且不存在不稳定对(即存在一对男女,他们更倾向于对方而不是自己的配偶)。 对于每个测试用例,我们首先读取人数n,并依次读取男性和女性的名字。然后,我们按照题目给定的顺序,依次读取每个男性和女性的首选列表。 下面是一个C语言实现的伪代码: ``` #include <stdio.h> #include <stdbool.h> #define MAX_N 27 typedef struct { int idx; // 男性在女性首选列表中的索引 int spouse; // 男性的配偶 int preferences[MAX_N]; // 女性的偏好列表 } Man; typedef struct { int idx; // 女性在男性首选列表中的索引 int spouse; // 女性的配偶 int preferences[MAX_N]; // 男性的偏好列表 } Woman; Man men[MAX_N]; Woman women[MAX_N]; int n; void stableMarriage() { // 初始化所有人未匹配 for(int i = 0; i < n; i++) { men[i].spouse = -1; women[i].spouse = -1; } // 进行稳定婚配算法 // 循环直到所有男性都成为稳定婚配 while(true) { bool finished = true; for(int i = 0; i < n; i++) { if(men[i].spouse == -1) { // 该男性未匹配 finished = false; int w = men[i].preferences[men[i].idx]; men[i].idx++; if(women[w].spouse == -1) { // 该女性未匹配 women[w].spouse = i; men[i].spouse = w; } else { int currentSpouse = women[w].spouse; if(women[w].preferences[i] < women[w].preferences[currentSpouse]) { men[currentSpouse].spouse = -1; women[w].spouse = i; men[i].spouse = w; } } } } if(finished) break; // 所有男性都已匹配 } } int main() { int t; scanf("%d", &t); // 输入测试的次数 while(t--) { scanf("%d", &n); // 输入人数 for(int i = 0; i < n; i++) { char name[2]; scanf("%s", name); // 读取男性名字 // 在这里可以将男性名字保存在men[i].name中 for(int j = 0; j < n; j++) { int preferences[2]; scanf("%d", &preferences[j]); // 读取男性偏好列表 men[i].preferences[j] = preferences[j]; } } for(int i = 0; i < n; i++) { char name[2]; scanf("%s", name); // 读取女性名字 // 在这里可以将女性名字保存在women[i].name中 for(int j = 0; j < n; j++) { int preferences[2]; scanf("%d", &preferences[j]); // 读取女性偏好列表 women[i].preferences[j] = preferences[j]; } } stableMarriage(); // 进行稳定婚配算法 // 输出每个男性及其配偶的配对情况 for(int i = 0; i < n; i++) { printf("%c %c\n", men[i].name, women[men[i].spouse].name); } } return 0; } ``` 以上是一个简单的实现稳定婚配问题的C语言程序,核心部分是稳定婚配算法,通过该算法可以得到每个男性及其配偶的配对情况。 注意:伪代码中的代码并未考虑边界情况和输入输出格式,实际使用时需要根据题目要求进行适当的调整。 ### 回答3: 题目要求通过给定的男性和女性的名字以及他们的首选列表,推导出稳定的婚配。 对于每个测试用例,我们可以使用“稳定婚配算法”来解决问题。具体步骤如下: 1. 首先,创建两个字典,分别用于存储男性的首选列表和女性的首选列表。字典的键是男性或女性的名字,值是一个列表,按照优先顺序列出了他们的首选对象。 2. 初始化一个空的字典,用于存储男性和女性的婚配结果。字典的键是男性的名字,值是他所匹配的女性的名字。 3. 创建一个队列,用于存储尚未分配配偶的男性。 4. 遍历所有的男性,将他们都加入队列中。 5. 进行以下循环,直到队列为空: - 从队列中取出一个男性。 - 获取他的首选女性列表。 - 遍历该列表,找到他尚未向其求婚的女性。 - 如果该女性没有婚配对象,则将男性和女性配对,并将匹配结果存入婚配字典中。 - 如果该女性已有婚配对象,则比较该女性对当前男性的偏好和她当前的配偶的偏好。 - 如果该女性更偏好当前男性,则替换她的配偶,并将原配偶重新加入队列。 - 如果该女性更偏好当前配偶,则继续遍历下一个女性。 6. 循环结束后,返回婚配字典即为稳定的婚配结果。 根据上述步骤,我们可以编写一个函数来实现稳定婚配问题的解决。函数的输入为男性和女性的名字列表以及他们的首选列表,输出为稳定的婚配结果。 这样,我们就可以通过此函数解决给定的稳定婚配问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值